1 条题解
-
0
题目题解
问题理解
给定 和 ,判断能否将 表示为恰好 个 2 的幂之和,并输出一种方案。
第一步:最小可能项数
将 写成二进制,每个为 的位对应一个 2 的幂。
例如 ,需要 项。
因此,最小项数等于 的二进制表示中 的个数,记作 。若 ,则不可能,输出
"NO"。
第二步:增加项数
如果 大于最小项数,我们可以将某个大于 的项 拆成两个 ,这样项数增加 。
例如:,项数从 变 。重复此过程,直到项数达到 。
第三步:可行性条件
由于每次拆分将一项变成两项,项数增加 ,最多可将所有项拆成 (不能再拆)。
因此,最大可能项数就是 (全部拆成 )。所以, 必须满足:
若 在此范围内,则一定可行。
第四步:构造方法
- 将 的二进制分解为若干 2 的幂,存入一个计数器
ans,同时将大于 的项放入队列q。 - 若当前项数
cnt小于 ,则从队列中取出一个大于 的项 ,将其拆成两个 :- 减少 的计数,增加 的计数。
- 若 ,将两个 都加入队列(以便后续继续拆分)。
- 项数
cnt增加 。
- 重复直到
cnt == k或队列为空。 - 若最终
cnt < k,则不可能(但根据条件应已判断),输出"NO"。 - 否则输出
"YES"和所有项。
第五步:时间复杂度
- 二进制分解:。
- 拆分操作最多 次。
- 总复杂度 ,满足 。
代码实现
#include <bits/stdc++.h> using namespace std; int main() { int n, k; cin >> n >> k; map<int, int> ans; queue<int> q; // 二进制分解 for (int i = 0; i <= 30; i++) { if (n >> i & 1) { int val = 1 << i; if (val > 1) q.push(val); ans[val]++; } } int cnt = ans.size(); if (cnt > k) { cout << "NO\n"; return 0; } while (cnt < k && !q.empty()) { int z = q.front(); q.pop(); ans[z]--; ans[z / 2] += 2; if (z / 2 > 1) { q.push(z / 2); q.push(z / 2); } cnt++; } if (cnt < k) { cout << "NO\n"; return 0; } cout << "YES\n"; for (auto [val, count] : ans) { for (int i = 0; i < count; i++) { cout << val << " "; } } cout << "\n"; return 0; }
验证样例
输入:
9 4输出:
YES 1 2 2 4(顺序可能不同,但满足要求)
输入:
8 1输出:
YES 8输入:
5 1输出:
NO输入:
3 7输出:
NO与题目输出一致。
总结
本题的关键在于:
- 最小项数为 ,最大项数为 。
- 通过不断拆分大于 的项,可以增加项数。
- 用队列维护可拆分的项,贪心拆分直到达到 。
- 将 的二进制分解为若干 2 的幂,存入一个计数器
- 1
信息
- ID
- 6340
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者