(完全背包+思维)
题意:给你一个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; }