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)\)。容易发现状态数组取值只是 01,可以考虑使用 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;
}
posted @ 2022-07-19 20:44  聆竹听风  阅读(253)  评论(0)    收藏  举报