C. Powers Of Two
https://codeforces.com/contest/1095/problem/C
题意:给定一个数n和一个数k,问n能否通过k个2的整数次幂相加得到?如果可以,则输出yes并输出这些数,否则输出no。
思路:只能想到暴力了 + 剪枝了!从lower_bound(),第一个最接近的n的二次幂数开始,依次往下找,直到找到1。核心的问题是剪枝:考虑几种情况:
1、如果剩下的数全部都用当前的幂次值,使用的数的数量会超过k,那就没必要继续递归了,因为再递归只会使用更多的数字。
2、如果剩下的数全部都用0作为幂,使用的数量不超过k,那么也没必要递归了,需要多补更大的数。
3、对于幂次=0的深度判定,可以不需要递归,直接O(1)判定一次就行了。
实测,开了1和3剪枝,可以AC,但是用时3s,总共限时4s。再加上2剪枝,时间可以缩减到200ms,所以这个题目,就是一个剪枝题!
总结:想了好久,没啥思路,dp也想了,组合数一大堆的,最后没想到用暴力可以ac,哎。太久没写代码了,递归的lambda有点手生,总结一下:
写法1:function<void(int, int, int)> dfs = [&](int i, int j, int k) {};
写法2: auto dfs = [&](auto&& self, int i, int j, int k){};
inline void solve(){
int n, k;
cin >> n >> k;
int q = 0;
while ((1ll << q) <= n) {
q ++;
}
q --;
bool ok = false;
auto dfs = [&](auto&& self, int sum, int p, int tot) {
if (tot == k && sum == n) {
ok = true;
cout << "YES\n";
return;
}
if (p < 0) {
return;
}
if (p == 0) {
if (n - sum == k - tot) {
cout << "YES\n";
for (int i = 0; i < k - tot; ++i) {
cout << 1 << ' ';
}
ok = true;
}
return;
}
int rem = n - sum;
int maxn = (rem - 1) / (1 << p) + 1;
if (maxn + tot > k) {
return;
}
if (rem + tot < k) {
return;
}
for (int i = 0; (1ll << p) * i <= rem; ++i) {
self(self, sum + (1 << p) * i, p - 1, tot + i);
if (ok) {
for (int j = 0; j < i; ++j) {
cout << (1 << p) << ' ';
}
return;
}
}
};
dfs(dfs, 0, q, 0);
if (!ok) {
cout << "NO\n";
}
}

浙公网安备 33010602011771号