背包(矩阵算法)详解

   最近两天整背包算法整的懵逼了,为什么懵逼?首先对着这个公式看了好久,如下:
                     f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}   f[v]=max{f[v],f[v-c[i]]+w[i]}

是不是觉得很懵逼?没错,看起来好像理解了,但是又不是很明白,直到我看到了如下的东西

   

体积

1

2

3

4

5

6

物品1

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

物品2

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

物品3

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

物品4

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值

当前重量和物品下的最大价值


然后问题就转化为了在矩阵里面填入满足条件的价值;即当前物品种类和当前重量下的最大价值;比如我们来看坐标(物品1,3)就代表,当存在物品1时,背包容量为3的情况下,价值最大是多少?
(物品2,3)就代表,当存在物品1,物品2是,背包容量为3的情况下,价值最大是多少?以此类推:
(物品4,6)代表存在物品1,2,3,4,背包容量为6的情况下,价值最大是多少;

理解了矩阵的含义,我们再来看公式,说白了就是根据矩阵前面的情况来决定矩阵当前格子内填的数目:
比如F [i][v],举个栗子,i=物品2,v=5时就是F[物品2][5],我们就在表格上找到这一格,当到了处理这一格时,就已经说明物品1那一行和物品2那一行的前4个格子都已经填上了数字,代表了当前物品种类下和背包容量下的最大价值;
接下来就是要决定F[物品2][5]这一格里面填什么了;
F[i-1][V]:我们填入实例就是F[物品1][5]的价值:背包为5的情况下,还没有放入物品2的最大价值是多少?就是F[物品1][5];
F[i-1][v-c[i]]+w[i]:首先这个位置有一个值得注意的点,变为F[i][V-c[i]]+w[i];那么则由一个物品变为了一类物品,C[i],我们这里理解为物品2的重量,W[i]我们理解为物品2的价值;因为这个时候我们要的应该是同时存在物品1情况下容量为(5-物品2体积)的价值,即F[物品1][5-物品2体积]这一格空格的值;
F[i][v-c[i]]+w[i]:即F[物品2][5-物品2体积]+物品2价值;F[物品1][5-物品2体积]代表了存在物品1和物品2情况下,体积为5-物品2体积情况下的最大价值;
那么MAX{ f[i-1][v],f[i][v-c[i]]+w[i]}就是比较{F[物品1][5],F[i-1][v-c[i]]+w[i]:即F[物品2][5-物品2体积]+物品2价值}谁的价值更大,那么 F[物品1][5]就填入哪个值代表重量为5的情况下,存在物品1和物品2情况下的价值最大值;

直到一直这样把这个矩阵填写完,那么矩阵的最后一行最后一列就是 有最多种类的物品和最大的背包容量下的最大价值是多少;附上算法源码:

Pakage **p:代表背包的二维数组,即矩阵本体;
ItemPakage* ItemEnum  代表物品种类的矩阵,即纵坐标
int volt:代表背包的体积容量,即横坐标的最大值

int MaxValue(Pakage **p,ItemPakage* ItemEnum,int nTypeNum,int nvolt)
{
   
for (int i=0; i<=nTypeNum; i++)
    {
        p[i][
0].nItem=0;
        p[i][
0].sumvolt=0;
    }
   
for (int j=0; j<=nvolt; j++)
    {
        p[
0][j].nItem=0;
        p[
0][j].sumvalue=0;
    }
   
   
for (int i=1; i<=nTypeNum; i++)
    {
      
         
for (int j=1;j<=nvolt;j++)
          {
           
if (j>=ItemEnum[i-1].Volt)
            {
               
if (p[i-1][j-ItemEnum[i-1].Volt].sumvalue+ItemEnum[i-1].Value>p[i-1][j].sumvalue)
                {
                    p[i][j]=p[i-1][j-ItemEnum[i-
1].Volt];
                    p[i][j].
sumvalue+=ItemEnum[i-1].Value;
                    p[i][j].
nItem=p[i][j].nItem+1;
                    p[i][j].
ArrItem[p[i][j].nItem-1]=ItemEnum[i-1];
                }
               
               
else
                {
                    p[i][j]=p[i-
1][j];
                }
            }
           
else
                p[i][j]=p[i][j-
1];
          }
        }
   
   
return p[nTypeNum][nvolt].sumvalue
;

}


最后就是需要注意边界条件了;因为二维矩阵的纵坐标是物品个数,横坐标为物品体积,都是从1开始的,但是矩阵本身初始记录位置是从0开始的,为了避免编程过程中思维的多次转换以及单独的边界条件处理(0的情况)建议简单化;4个物品体积为8的问题,不是开(4x8)32个格子的矩阵而是要开(5X9)45个格子的矩阵,涉及到0的位置全部填0;
接下来就是愉快的编写算法了!
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值