背包问题九讲(01背包和完全背包问题)

是今天逛B站的时候,看到的一个up主上课方式nice。
所以就准备听听他的课,来学学这些个知识:up主戳着能看到

1.01背包
在这里插入图片描述
在这里插入图片描述
首先F[I][J]代表的是,在拥有i件物品,且最大体积为J的时候,拥有的最大价值。
也就是在拥有F[i][0–j] 也就是求在I件物品当中,体积从0到v 的最大值(其实我在想,为什么不直接就是F[i][j]呢,还要在体积变化的情况之下写)

而怎么求F[I][J]=MAX(F[I-1[J],F[I-1][J-VJ]+WJ)
也就是对第i物品的考虑,要么不选,就等于前i-1件物品,在体积为j的情况下的最大值,要么选,也就是在前i-1件物品上,体积减少第i件物品的体积,在加上第i件物品的价值的总价值。

//二维数组的模式:
(这是伪代码)

  for(int i=1;i<=n;i++)//n是物品件数
    for(int j=0;j<=m;j++){//m是总价值
        f[i][j]=f[i-1][j];//先是不选第i件,这也解释了为什么i要从1开始
        if(j>v[i])
            f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i])//也就是在判断可以选第i件的时候,选其中的最大值
    }
    //然后两次for循环之后,来找最大值
    int re;
    for(i=0;i<=m;i++)
        re=max(re,f[n][i]);

然后就是一维数组模式,也算是一个难点吧,从二维变成一维。就是用到了滚动数组的概念
其实只要记住一句话,就是在二维的
f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i])//也就是在判断可以选第i件的时候,选其中的最大值
这个部分变成一维的
f[j]=max(f[j],f[j-v[i]]+w[i])
要保证j-v[i]没被算过的,就是需要J逆序,也就是从大的到小的,不然如果从正序开始的话,前面那些小的值已经都改变过了,而大的值会因为小的值被改变过,而经过二次改变。
参考了https://blog.csdn.net/xiajiawei0206/article/details/19933781
所以变成一维的情况就是:

for(int i=1;i<=n;i++)//n是物品件数
    for(int j=m;j>v[i];j--){//m是总价值
       //这个被覆盖掉了 f[j]=f[i-1][j];
        //这个条件在for循环体现了if(j>v[i])
            f[j]=max([j],f[j-v[i]]+w[i])//也就是在判断可以选第i件的时候,选其中的最大值
    }
    //然后两次for循环之后,来找最大值
    int re;
    for(i=0;i<=m;i++)
        re=max(re,f[i]);

2.完全背包问题

在这里插入图片描述
完全背包问题,关键在于每个物品可以用无限次

这个是二维的形式

   dp[i][v]=max(dp[i-1][v],dp[i][v-v[i]+w[i]);
    //当不选第I件的时候,那就是dp[i-1][v]
    //而当可以选第i件的时候,与01背包不同之处在于,可以选多件,直到体积不够
    //所以是dp[i][v-v[i]+w[i]

这个是正常思维的伪代码:

   //思路就是可以取多件 
    for(int i=0;i<=n;i++)//n是物品件数
    for(int j=m;j>=v[i];j--){//逆序开始,并且要小于当前物品件数的最大值
        for(int k=0;k*v[i]<=j;k++){//k体现的是,第i件物品可以取多少个
            f[j]=max(f[j],f[j-k*v[i]+k*w[i]);
        }
        
    }

然后是这个Up主说的,一种很巧妙的方法

  for(int i=0;i<=n;i++)//n是物品件数
    for(int j=v{i};j<=m;j++){//逆序开始,并且要小于当前物品件数的最大值
            f[j]=max(f[j],f[j-v[i]+w[i]);
        
        
    }

多重背包问题
关键是在于每个物品使用的数量是次数的限制的
所以可以在01背包的情况之下,再加一层对数据的限制

   //思路就是可以取多件,且有数量限制
    for(int i=0;i<=n;i++)//n是物品件数
    for(int j=m;j>=v[i];j--){//逆序开始,并且要小于当前物品件数的最大值
        for(int k=0;k*v[i]<=j&&k<=s[i];k++){//k体现的是,第i件物品可以取多少个,s是当前值最多的个数
            f[j]=max(f[j],f[j-k*v[i]+k*w[i]);
        }
        
    }
发布了88 篇原创文章 · 获赞 5 · 访问量 3545

猜你喜欢

转载自blog.csdn.net/qq_41115379/article/details/105092510