题意:给你一个n和k,分别表示一共有n种物品(每种都有无限个),你的背包可以装下k个(且必须装满),随后给出n个数a[i]表示每种物品的价值,要求输出能装出的所有价值的情况。
题解:从题意可以知道我们可以装的价值情况在(最小价值物品)min_x * k 到 (最大价值物品) max_x * k 之间,不难想到这是一道完全背包问题(物品无限),那么如何转换到完全背包呢?这里我们只需要对每个物品的价值a[i] 减去 min_x 即可,那么我们的背包就是从0开始的了。定义dp[i]表示价值为i的物品所需要的最少物品(只有能装的越多才可能价值越大)且这里的i的范围就是max_x * k了,运行一波完全背包,取出dp[i]<=k的就是答案了(因为初始状态的0是全都是最小价值的情况,你最多只能通过替换k次去得到其他答案;也可以理解为当dp[i]<k时,说明该情况可以获得,剩余的用min_x来补),最后输出加上min_x*k即可。
代码如下:
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<cstdio>
#include<time.h>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn = 1e6 + 500;
int a[maxn];
int dp[maxn];
vector<int>ans;
int main() {
int n, k;
while (~scanf("%d%d", &n, &k)) {
ans.clear();
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
sort(a, a + n);
n = unique(a, a + n) - a;//去重
int min_x = a[0];
for (int i = 0; i < n; i++)//都减去min_x得到的答案范围就0~k*(max_x-min_x)内的完全背包的所有答案
a[i] -= min_x;
int max_x = a[n - 1];
for (int i = 1; i <= max_x*k; i++)//完全背包2:需要刚好装满的初始化方法
dp[i] = inf;
dp[0] = 0;
for (int i = 1; i < n; i++)
for (int j = a[i]; j <= max_x*k; j++)
dp[j] = min(dp[j], dp[j - a[i]] + 1);
for (int i = 0; i <= max_x*k; i++)//当dp[i]<k时,说明该情况可以获得,剩余的用min_x来补
if (dp[i] <= k) ans.push_back(min_x*k + i);
for (int i = 0; i < ans.size(); i++)
printf("%d%c", ans[i], (i == ans.size() - 1) ? '\n' : ' ');
}
return 0;
}