01背包问题

参考链接:

  1. 总结——01背包问题 (动态规划算法)
  2. 01背包问题

问题描述

有 n 件物品,每件物品的重量为 w[i],价值为 c[i]。现有一个可承载的最大重量为 V 的背包,问如何选取物品放入背包,使得背包内的物品的总价值最大。注意:每种物品都只有一件。

解析

  1. 面对每个物品,我们只有两种选择:拿与不拿;
  2. 声明一个二维数组m[i][j]m[i][j] 表示在面对(注意这里是面对这件物品,不一定要拿它)第 i 件物品,且背包容量为 j 时所能获得的最大价值;
    1. j < w[i] 时,背包容量不足以放下这件物品,只能选择不拿,此时面对第 i 件物品做出决策后的背包的价值为 m[i][j] = m[i-1][j]i-1 表示上一次选完的价值,而此次决策后因为没有拿物品,所以 j 没有变化;
    2. j >= w[i] 时,背包容量可以放下这件物品。此时要考虑的是放入这件物品以后,背包的价值是不是可以获得更大价值(因为此时背包容量变大了,之前选的物品可以重新调整,也许选出来的物品总价值更大)。
      1. 如果拿取,则背包的价值为 m[i][j] = m[i-1][j-w[i]] + c[i]i-1 表示在决策了上一件物品后的价值,而这个价值还与背包的容量有关,因为要考虑 j - w[i],此时要拿取这件物品,所以必须容量必须预留出 w[i] 大小,得出这个价值以后,加上 c[i] 则为拿取这件物品后的背包总价值;
      2. 如果不拿取,则背包的价值为 m[i][j] = m[i-1][j];与背包放不下这个物品的情况相同;
      3. 比较拿与不拿的情况下,背包的价值哪个更大。

综上所述,可以得到一个状态转移方程如下:

if (j >= w[i])
    m[i][j] = max(m[i-1][j-w[i]]+c[i], m[i-1][j]);
else
    m[i][j] = m[m-i][j];

示例

价值数组c[] = {8, 10, 6, 3, 7, 2},与之对应的重量数组w[] = {4, 6, 2, 2, 5, 1},求背包可承载最大重量V = 12时背包最大价值为多少?

解答如下:
根据以上的分析,可以列出如下表格:第一行为(0 ~ 12)表示背包的容量V从0累加到12;第一列(1 ~ 6)表示第几个物品的重量,得出的值为m[i][j];表格如下:

0123456789101112
1000888888888
20008810101010181818
30668814141616181824
40669914141717191924
50669914141717192124
626891114161719192124

表格数值解析(横向):

  1. 背包容量为0时,价值为0,这里不计入讨论;
  2. 考虑第1件物品,重量为4,因此在背包容量从1递增到3时,背包价值均为0;直到背包容量递增到4时,此时第1件物品可以放入,因此背包价值为8,此后背包容量再继续递增,由于目前只有第1件物品可以考虑,因此后面容量再大,背包的价值依然为8;
  3. 考虑第2件物品,重量为6,因此背包容量在1~3时,背包价值均为0;
    1. 在背包容量递增到4时,此时可以考虑的物品有第1件和第2件,因此第2件物品放不下时,还可以选择第2件放进去,因此背包容量为4~5时,背包的价值和只考虑第1件物品的价值是一致的,均为8;
    2. 当背包的容量递增到6时,此时依然选择放第1件物品,剩余容量为2,放不了其他物品(第2件物品重量为6,放不进去),背包的价值为8;但如果放第2件物品,剩余容量为0,放不了其他物品(第1件物品重量为4),此时背包价值为10。明显放第2件物品价值更高,因此背包容量为6时,最大价值为10;
    3. 背包容量依次递增,从6递增到10时,最大价值均为9;
    4. 当背包容量递增到10,此时可以发现,两件物品刚好放得下,此时背包最大价值为18;
    5. 背包容量递增至12时,情况如与容量为10相同。
  4. 考虑第i件物品的情况如上依次类推;

如果以上的方式比较难懂的话,换个方向考虑一下,纵向的数值填写思路如下:

  1. 背包容量为0时,价值为0,这里不计入讨论;
  2. 当背包容量为1时,从头到尾(表格为从上至下)考虑物品是否能够放入。发现只有第6件物品的重量符合,因此其他物品的情况下,背包价值均为0,第6件物品的情况下,背包价值为2;
  3. 当背包容量为2时,从第1件物品考虑,1、2件物品的重量太大,无法放入,因此它们对应的情况下的背包价值为0;当考虑第3件物品时,发现刚好可以放入,此时背包价值为6;依次考虑第4件物品,第4件物品的重量也为2,因此放第3件或者第4件的价值都是一样的,均为6;考虑第5、第6件物品时,发现5、6物品都无法放下背包,但因为前面可以放3或4了,所以5、6物品对应的价值为6(这两种情况相当于只放物品3或物品4);
  4. 背包容量依次递增,价值推算如上类推即可。

由此可以得出,整个问题的最优解为二维数组的最后一个元素,即最大价值为24,此时对应的有多重选择方案。

代码如下:

#include <iostream>
#include <cstring>

using namespace std;

const int N=15;
 
int main()
{
    int v[N]={0,8,10,6,3,7,2};
    int w[N]={0,4,6,2,2,5,1};

    int m[N][N];
    int n=6,c=12;
    memset(m,0,sizeof(m));
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=c;j++) {
            if(j>=w[i])
                m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
            else
                m[i][j]=m[i-1][j];
        }
    }

    for(int i=1;i<=n;i++) {
        for(int j=1;j<=c;j++) {
            cout<<m[i][j]<<' ';
        }
        cout<<endl;
    }
    
    return 0;
}

运行后打印如下:

root@root:~/Workspace/MyTest# ./a.out 
0 0 0 8 8 8 8 8 8 8 8 8 
0 0 0 8 8 10 10 10 10 18 18 18 
0 6 6 8 8 14 14 16 16 18 18 24 
0 6 6 9 9 14 14 17 17 19 19 24 
0 6 6 9 9 14 14 17 17 19 21 24 
2 6 8 9 11 14 16 17 19 19 21 24 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值