洛谷P4141 消失之物 题解 背包问题扩展

题目链接:https://www.luogu.com.cn/problem/P4141

题目大意:
\(n\) 件物品,求第 \(i\) 件物品不能选的时候(\(i\)\(1\)\(n\))0-1背包方案数。

解题思路:

传统方法

遍历每一遍不选的物品,然后对剩余的物品求01背包方案数。
时间复杂度为 \(O(n^2m)\)

由于这里每一件物品的价值和时间相同,
所以可以定义状态 \(f[i]\) 表示填满体积 \(i\) 的方案数。

则有状态转移方程:
\(f[0] = 1\)
\(f[i] = \sum f[i-c]\) (其中 \(c\) 对应每一件物品的体积)

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2020;
int n, V, w[maxn], f[maxn];
void pack(int c) {
    for (int i = V; i >= c; i --)
        f[i] = (f[i] + f[i-c]) % 10;
}
void solve(int id) {
    memset(f, 0, sizeof(f));
    f[0] = 1;
    for (int i = 0; i < n; i ++) {
        if (i == id) continue;
        pack(w[i]);
    }
    for (int i = 1; i <= V; i ++) cout << f[i];
    cout << endl;
}
int main() {
    cin >> n >> V;
    for (int i = 0; i < n; i ++) cin >> w[i];
    for (int i = 0; i < n; i ++) solve(i);
    return 0;
}

优化

转载自 Kelin大神的博客

其实只要跑一次背包(全部物品都在)
然后我们考虑少了某个物品怎么办?
我们在01背包DP时会用到这个转移

for(int j=m;j>=w[i];--j)
    f[j]+=f[j-w[i]];

我们少了i物品就是在原来的基础上少了一次这样的转移
所以我们在原来的基础上顺推减去这样的一次转移就ok了

memcpy(g,f,sizeof f);
for(int j=w[i];j<=m;++j)
    g[j]-=g[j-w[i]];

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2020;
int n, V, w[maxn], f[maxn], g[maxn];
void pack(int c) {
    for (int i = V; i >= c; i --)
        f[i] = (f[i] + f[i-c]) % 10;
}
void unpack(int c) {
    memcpy(g, f, sizeof(f));
    for (int i = c; i <= V; i ++)
        g[i] = (g[i] - g[i-c] + 10) % 10;
}
void init() {
    memset(f, 0, sizeof(f));
    f[0] = 1;
    for (int i = 0; i < n; i ++) {
        pack(w[i]);
    }
}
void solve(int id) {
    unpack(w[id]);
    for (int i = 1; i <= V; i ++) cout << g[i];
    cout << endl;
}
int main() {
    cin >> n >> V;
    for (int i = 0; i < n; i ++) cin >> w[i];
    init();
    for (int i = 0; i < n; i ++) solve(i);
    return 0;
}
posted @ 2019-12-11 20:07  quanjun  阅读(144)  评论(0编辑  收藏  举报