【题解提供者】吴立强
解法
思路
三个条件:1、钱袋数最小;2、能够构成 1~n 的所有数;3、不能有钱数相同的钱袋(1 除外)。
现在只考虑前两个条件,构造的序列非常简单:1, 2, 4, 8, …, 2 k 2^k 2k, g g g(其中 g = n − ∑ i = 0 k 2 i g = n- \sum_{i=0}^k2^i g=n−∑i=0k2i)。且有 g < 2 k + 1 g < 2^{k+1} g<2k+1 成立(否则应该继续拆分)。
首先,第二点容易证明,这里不予证明。
第一点可以这么看,根据二进制相关知识可以发现 1 , 2 , 4 , . . . , 2 k {1,2,4,...,2^k} 1,2,4,...,2k 可以构成小于 2 k + 1 − 1 2^{k+1}-1 2k+1−1 的所有数,特别的,若需要构成的数超过 g g g 那么先把 g g g 加进去,剩下的由上述序列补齐即可。
现在考虑第三点。若有 g = 1 g=1 g=1,则符合条件。若有 g g g 不为 2 的某次方,则符合条件。若 g g g 是不为 1 的 2 的某次方,那么可以构造 1 , 2 , 4 , . . . , g − 1 , g + 1 , 2 × g , 4 × g , . . . , 2 k {1, 2, 4, ..., g-1, g+1, 2\times g, 4\times g,...,2^k} 1,2,4,...,g−1,g+1,2×g,4×g,...,2k 即可(小于 g g g 的可以组成, g g g 可以组成,大于 g g g 的可以组成)。
所以答案即为上述构造序列的长度,也可以理解为在 2 进制表示下 n n n 的位数。
代码展示
#include <iostream>
using namespace std;
int main() {
int n, ans = 0; cin >> n;
while(n) {
ans ++;
n >>= 1;
}
cout << ans;
return 0;
}
算法分析
程序时间复杂度为 O ( log 2 n ) O(\log_2n) O(log2n)。
拓展
本题涉及程序设计竞赛中的常用技巧:合理推论,证明推论可以成立,证明推论是最优解。