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";
    }
}
posted @ 2025-03-13 16:53  _Yxc  阅读(12)  评论(0)    收藏  举报