比赛链接:
https://ac.nowcoder.com/acm/contest/24872
I:
有 \(n\) 件物品,第 \(i\) 件物品的体积是 \(t_i\),价值是 \(v_i\),最多选出 \(k\) 件使它们的体积翻倍,然后选出若干件物品将其分为 体积和 相同的两堆,求选出物品的价值之和最大值
思路:
从 \(n\) 件物品中选出若干件物品,可以想到 背包。
由于要分成 两堆,我们可以假定,放入 第一堆 中是加入背包,放入 第二堆 中是拿出背包,也就是说放入第一堆中是 增加体积,放入第二堆中 减少体积,不论放入哪一堆,价值 都是增加的。
dp[i][j][p]为 \(dp\) 方程,\(i\) 表示当前选择的是第几件物品, \(j\) 表示选中几件物品使其 体积翻倍,\(p\) 为 背包体积,就是一个简单背包啦。
可以发现,在求解的过程中,每一个物品只需要从上一个物品的状态转移过来,于是我们可以通过 滚动数组 优化掉 第一维度。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 105;
const int W = 1310;
LL n, k, w[N], v[N], f[N][W * 2], g[N][W * 2], ans = -(1LL << 60);
int main(){
scanf("%lld %lld", &n, &k);
for (int i = 1; i <= n; i++)
scanf("%lld %lld", &v[i], &w[i]);
memset(f, 233, sizeof(f));
f[0][W] = 0;
for (int i = 1; i <= n; i++){
memcpy(g, f, sizeof(g));
for (int j = 0; j < i && j <= k; j++)
for (int p = 0; p < W * 2; p++){
if (p > w[i]) g[j][p - w[i]] = max(g[j][p - w[i]], f[j][p] + v[i]);
if (p > 2 * w[i]) g[j][p - 2 * w[i]] = max(g[j][p - 2 * w[i]], f[j][p] + v[i]);
if (p + w[i] < W * 2) g[j][p + w[i]] = max(g[j][p + w[i]], f[j][p] + v[i]);
if (p + 2 * w[i] < W * 2) g[j][p + 2 * w[i]] = max(g[j][p + 2 * w[i]], f[j][p] + v[i]);
}
memcpy(f, g, sizeof(f));
}
for (int i = 0; i <= k; i++)
ans = max(ans, f[i][W]);
cout << ans << "\n";
return 0;
}