题目描述
一天,CC买了N个容量可以认为是无限大的瓶子,开始时每个瓶子里有1升水。接着~~CC发现瓶子实在太多了,于是他决定保留不超过K个瓶子。每次他选择两个当前含水量相同的瓶子,把一个瓶子的水全部倒进另一个里,然后把空瓶丢弃。(不能丢弃有水的瓶子)
显然在某些情况下CC无法达到目标,比如N=3,K=1。此时CC会重新买一些新的瓶子(新瓶子容量无限,开始时有1升水),以到达目标。
现在CC想知道,最少需要买多少新瓶子才能达到目标呢?
输入输出格式
输入格式:
一行两个正整数, N,K。
输出格式:
一个非负整数,表示最少需要买多少新瓶子。
这道题很有意思,所以要在这里好好的写一下题解。
由“开始时每个瓶子里有1升水”和“新瓶子容量无限,开始时有1升水”、“他选择两个当前含水量相同的瓶子,把一个瓶子的水全部倒进另一个里”,很容易联想到但凡不是空瓶子,每个瓶子里水的数量一定都是以2为底的幂,如1、2、4、8、16、32、64……等等。由此我们可以将这些数字放到二进制视角下来思考。
由题中说“他决定保留不超过K个瓶子”,可将其转化为“CC最终所具有的瓶子总数 n_f 在二进制表示形式下有总计不超过K个1”。
(下面说的所有东西可能都不太好想,请认真思考。)
然而我们需要买空瓶子……所以结果的数字只能大不能小,因此我们需要通过去除数字末尾的1,保留数字前面的1来使数字达到要求。
除了Lowbit之外,我还有一种做法(这个做法其实就是Lowbit和加综合起来的底层实现)。这个做法分为三步:
1、找到数字中倒数第一个1.
2、把接下来从后向前连续的所有1变成0.
3、再把接下来不是1的第一个0变成1.
举例:原数:001011
第二步完成后:001000
第三步完成后:001100
重复进行这一操作,直到满足要求“瓶子总数 n_f 在二进制表示形式下有总计不超过K个1”即可。
程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include<iostream> #include<algorithm> #include<cstdio> #include<string> #include<deque> #include<map> #include<cstring> using namespace std; inline int & setBit(int & v, int sv, int p) { if (sv) { v |= 1 << p; } else { v &= ~(1 << p); } return v; } inline int getBit(int v, int p) { return (v >> p) & 1; } bool checkBits(int value, int num) { int cnt = 0; for (int i = 0; i < 32; i++) { cnt += getBit(value, i); } return cnt <= num; } int main() { int n, k; cin >> n >> k; int no = n; while (!checkBits(n, k)) { int ptr = 0; while (!getBit(n, ptr))ptr++; while (getBit(n, ptr)) { setBit(n, 0, ptr); ptr++; } setBit(n, 1, ptr); } cout << n-no; } |