HDU7140. Backpack (2022杭电多校第1场C题)
HDU7140. Backpack (2022杭电多校第1场C题)
题意
有一个容量为 \(m\) 的背包。
有 \(n\) 个物品,第 \(i\) 个物品的体积为 \(v_i\),价值为 \(w_i\)。
找到一种方案,恰好装满大小为 \(m\) 的背包,且价值的 异或和 最大。
若找不到恰好装满大小为 \(m\) 的背包,则输出 -1。
分析
容易想出一个 \(O(nm \cdot 2^{10})\) 的dp,设 dp[i][j][k] 为考虑前 \(i\) 个物品,恰好装了价值异或和为 \(j\),体积之和为 \(k\) 的物品 是否可行。 可行为 1,不可行为 0。
容易写出转移方程
if (k >= v[i])
dp[i][j][k] = dp[i-1][j][k] | dp[i-1][j ^ w[i]][k - v[i]];
else
dp[i][j][k] = dp[i-1][j][k];
但是复杂度过高,却又不是高太多 \((10^9)\)。容易发现状态数组取值只是 0 或 1,可以考虑使用 bitset 进行优化。
再次观察转移方程,如果我们把 dp[i][j] 当成一个 01 串的话,实际上就是将dp[i-1][j^w[i]] 左移 v[i] 位的结果 与 dp[i-1][j] 进行按位或运算。这样的话转移一次的复杂度从 \(O(m)\) 变成了 \(O(\frac{m}{w})\), 其中 \(w\) 是字长。
这样总复杂度就变为 \(O(\frac{2^{10} nm}{w})\)
代码
#include <bitset>
#include <iostream>
using namespace std;
const int maxn = (1 << 10) + 10;
int n, m;
int v[maxn], w[maxn];
bitset<maxn> dp[2][maxn];
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> v[i] >> w[i];
}
for (int i = 0; i < 1024; i++) {
dp[0][i].reset();
dp[1][i].reset();
}
int t = 0;
dp[t ^ 1][0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < 1024; j++) {
dp[t][j] = dp[t ^ 1][j] | (dp[t ^ 1][j ^ w[i]] << v[i]);
}
t ^= 1;
}
int ans = -1;
for (int i = 0; i < 1024; i++) {
if (dp[t ^ 1][i][m]) {
ans = i;
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号