CF632E
比较巧妙的题
注意到题目要求是取恰好 \(k\) 个物品能搜出所有的权值和,这比较麻烦。(直接完全背包的话连样例都过不了)
参考样例找找原因:
3 2
1 2 3
完全背包求出 \(f_3=1\),即凑出 \(3\) 所需最小物品数量是 \(1\),但是它也是可以被两个物品凑出的,所以应该被输出。
那么有没有办法把 \(1\) 个物品扩充为 \(2\) 个物品呢?
也就是说需要存在价值为 \(0\) 的物品,这样就可以塞进去了。
但是 \(1\le a_i\le 1000\) 啊
那么先按价值从小到大排个序,然后给每个物品的价值减去最小价值。这样就存在价值为 \(0\) 的物品了。
然后就可以 DP 了,设 \(f_{i}\) 表示凑出 \(i\) 价值需要的最小物品数量。
最终输出答案时,扫一遍 \(f\),如果 \(f_{i}\le k\),就输出 \(k\times mn+i\)。
时间复杂度为 \(\mathcal O(n^3)\),但是时限 \(5s\) 所以很快啊,就过了。
具体细节看代码。
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 1005, M = 1000000;
int n, k;
int a[N], b[N];
int f[N*N];
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
sort(a + 1, a + n + 1); for (int i = 1; i <= n; ++i) b[i] = a[i] - a[1];
memset(f, 0x3f, sizeof f), f[0] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = b[i]; j <= M; ++j)
f[j] = min(f[j], f[j - b[i]] + 1);
}
for (int i = 0; i <= M; ++i) if (f[i] <= k) printf("%d ", k * a[1] + i);
return 0;
}

浙公网安备 33010602011771号