背包问题-背包九讲阅读笔记

本文详细介绍了背包问题的多种类型,包括0-1背包、完全背包、多重背包、混合背包问题、二维费用背包问题以及分组背包问题。通过状态转移方程和优化技巧展示了如何解决这些问题,并给出了具体示例和代码实现。对于复杂场景,如有依赖的背包问题,文章同样给出了解决方案。此外,还讨论了泛化物品的概念,将其与之前介绍的背包问题类型进行了联系。
摘要由CSDN通过智能技术生成

背包问题

0-1背包

这是最简单的背包问题,简而言之就是每件物品只有一样,可以取或者不取,对于容量为V的背包,N件物品,每一件都可以尝试放入背包中,那么顺序就是遍历这N件物品,每遍历到一个新的物品,都尝试将当前物品放入背包中,看看是放入后得到的价值高还是不放的价值高,取最高者即可。

所以需要一个二维数组w[N][V],这个二维数组中的每一个位置w[i][j]代表的意思是前i个物品在容量为j的时候可以获取的最大价值是多少。那么选当前物品(重量W,价值Va)得到的价值就是w[i - 1][j - W] + Va,不选当前物品的价值为w[i - 1][j],选最大值就行。

状态转移方程为:

w[i][j]=max(w[i1][jW]+Va,w[i1][j]) w [ i ] [ j ] = m a x ( w [ i − 1 ] [ j − W ] + V a , w [ i − 1 ] [ j ] )

题目:编号a,b,c,d,e的5件物品,重量分别是2,2,6,5,4,价值分别是6,3,5,4,6,现在背包承重为10,如何使装入背包的物品具有最大值?

如果采用状态转移方程,我们可以知道二维数组的值为:

0 1 2 3 4 5 6 7 8 9 10
a 0 0 6 6 6 6 6 6 6 6
b 0 0 6 6 9 9 9 9 9 9
c 0 0 6 6 9 9 9 9 10 13
d 0 0 6 6 9 9 9 10 11 13
e 0 0 6 6 9 9 12 12 15 15

代码如下:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//二维数组求0-1背包
int main()
{
    int n, m;   //n:物品个数,m:背包容量
    while (cin >> n >> m) {
        vector<pair<int, int>> vw(n + 1);   //价值+重量
        for (int i = 1; i <= n; ++i)
            cin >> vw[i].first >> vw[i].second;
        vector<vector<int>> ret(n + 1, vector<int>(m + 1, 0));  //计算数组,选择或者不选择
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                if (vw[i].second > j) { //背包容量还不够装当前的东西
                    ret[i][j] = ret[i - 1][j];  //那就只能放可以放的(就是上一行,不包括当前物品的情况)
                }
                else {  //可以装当前物品,就看看在没有当前物品的前提下(就是参考上一行),装和不装哪个更好
                    ret[i][j] = max(ret[i - 1][j], ret[i - 1][j - vw[i].second] + vw[i].first);
                }
            }
        }
        cout << ret[n][m] << endl;
    }

    return 0;
}

这里可以优化到一维数组,这样可以减少空间复杂度,具体方法是将第二层循环颠倒,从容量为满到空进行反向计算,这样每次循环的时候,面对的背包都是不包含当前物品的背包,使用数值进行计算即可。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
    int n, m;   //n:物品个数,m:背包容量
    while (cin >> n >> m) {
        vector<pair<int, int>> vw(n + 1);   //价值+重量
        for (int i = 1; i <= n; ++i)
            cin >> vw[i].first >> vw[i].second;
        vector<int> ret(m + 1, 0);  //一维数组,反向求0-1背包
        for (int i = 1; i <= n; ++i) {
            for (int j = m; j >= 1; --j) {
                if(vw[i].second <= j)
                ret[j] = max(ret[j], ret[j - vw[i].second] + vw[i].first);  //去掉当前物品的重量+当前物品的价值和原价值相比
            }
        }
        cout << ret[m] << endl;
    }
    return 0;
}

完全背包

完全背包问题和0-1背包问题不同的地方在于,完全背包的每件物品都是无穷多件,这样我们就不用像0-1背包一样,每次都选择不包括当前物品的背包来尝试添加当前物品(因为每件物品只能添加一次),而是可以在包括当前物品的背包中,选择继续添加当前物品。

所以需要一个一维数组w[N],这个数组中的每一个位置w[i]代表的意思是在容量为j的时候可以获取的最大价值是多少。那么添加当前物品(第一次或是多次)(重量W,价值Va)得到的价值就是w[i - W] + Va,不添加当前物品的价值为w[i],选最大值就行。

状态转移方程为:

w[i][j]=max(w[i][jW]+Va,w[i][j]) w [ i ] [ j ] = m a x ( w [ i ] [ j − W ] + V a , w [ i ] [ j ] )

那么可以用一维数组来存储背包的容量产生的最大价值,设数组为f[m],那么f[i]代表i容量的背包可以产生的最大价值。

牛客网-牛牛炒股票

题目:牛牛得知了一些股票今天买入的

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值