多重背包

题目大意:



这个问题和 01背包 问题很相似,我们也可以依然采取 01背包 的状态定义

dp[i][j] 代表前 i 个物品 容量为 j 的背包的最大价值

那么状态转移方程也就出来了:

dp[i][j] = max(dp[i][j],dp[i-1][j-k*v[i]]+w[i])        (k 可以为 0,1,2,3...s[i])

int dp[110][110],v[110],w[110],s[110];

int main() {
    int n,m;
    cin >> n >> m;
    for (int i = 1;i <= n;i++) {
        cin >> v[i] >> w[i] >> s[i];
    }
    for (int i = 1;i <= n;i++) {
        for (int j = 1;j <= m;j++) {
            for (int k = 0;k <= s[i];k++) {
                if (k * v[i] <= j)
                    dp[i][j] = std::max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
            }
        }
    }
    cout << dp[n][m] << endl;
    return 0;
}

这种朴素的方法的时间复杂度 和 空间复杂度都很高,所以一般不采取

我们可以先采取和 01背包 一样的滚动数组的方式去优化一下

简单优化:

和 01背包 一样,我们也应该从大往小更新,理由和 01背包 也是一样的,这里就不多说了

int dp[1010];

int main() {
    int n,m;
    std::cin >> n >> m;
    for (int i = 1;i <= n;i++) {
        int v,w,s;
        std::cin >> v >> w >> s;
        for (int j = m;j >= 0;j--) {
            for (int k = 1;k <= s && k * v <= j;k++) {
                dp[j] = std::max(dp[j],dp[j-k*v]+k*w);
            }
        }
    }
    std::cout << dp[m] << std::endl;
    return 0;
}

二进制优化:拆解成多个 01背包问题

其实就是把 件数s 进行二进制的拆分 ,因为这样拆分之后的数 可以表示 [0~s] 之间的任何数  (自行证明)

int dp[2010];

struct Node {
    int v,w;
};
std::vector<Node> vec;

int main() {
    int n,m;
    std::cin >> n >> m;
    for (int i = 1;i <= n;i++) {
        int v,w,s;
        std::cin >> v >> w >> s;
        for (int k = 1;k <= s;k <<= 1) {
            s -= k;
            vec.push_back({k*v,k*w});
        }
        if (s > 0)
            vec.push_back({s*v,s*w});
    }
    for (auto i:vec) {
        for (int j = m;j >= i.v;j--) {
            dp[j] = std::max(dp[j],dp[j-i.v]+i.w);
        }
    }
    std::cout << dp[m] << std::endl;
    return 0;
}

这个时候的算法复杂度以及足够优秀了  ,当然还有一种更加优秀的做法就是 采取单调队列优化 (自行搜索)

原文地址:https://www.cnblogs.com/-Ackerman/p/12250417.html