比賽鏈接:
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;
}