01背包,多重背包,完全背包——总结

可以先看下这篇博客理解下动态规划的思想:初识动态规划
写在前面的:这篇博客主要写的是,一个容量为v的背包去装n个物品能获得的最优解的问题

问题简述:现在有一个背包,它能容纳的最大重量为v,问背包所能带走的最大价值是多少?
01背包:有n个物品,每个物品的重量为w[i],每个物品的价值为h[i]。
[对于每个物品不可以取多次,最多只能取一次,之所以叫做01背包,0表示不取,1表示取]
多重背包:有n种物品,每个物品的重量为w[i],每个物品的价值为h[i],每种物品有c[i]个。
完全背包:有n种物品,每个物品的重量为w[i],每个物品的价值为h[i],每种物品有无限个。

1. 最基础的二维数组解01背包问题

设dp[i][j]表示处理到第i件物品时,容量为j的背包能获得的最大价值,处理结束后dp[n][v]就是我们所要求解的值。背包问题第一层循环都是跑的物品总类数(目前我遇到的都是这样子),一类一类地处理n类物品。当我们处理到第i件物品时,我们知道前i-1件物品在背包容量为1、2、3、、、n时能获得的最大价值分别是dp[i-1][1]、dp[i-1][2]、dp[i-1][3]、、、dp[i-1][v]。既然是01背包,我们对第i件物品就有两种策略:不取——那就好办了,容量为j的背包所能装的价值在i-1的基础上不变,就是dp[i-1][j];取——容量为j的背包就得腾出w[i]的空位放物品i,此时的价值就为dp[i-1][j-w[i]]+h[i],所以dp[i][j]的最优解就是在这两种情况之间,dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+h[i])。就这样,跑完第二层循环我们就可以得到dp[i][j]的值(j=1、2、3、、、v)。
注意:我们对第i件物品的处理都是基于前i-1件物品的存放结果的,而前i-1件物品的存放结果第i件物品没有参与,这也是01背包问题一维优化的关键点。
—其实跑完了之后我们会发现,我们可以得到背包容量在1~v范围内的时去装这n件物品能获得的最大价值。
—后面的都是根据这个衍生出来的,而且后面完全背包和多重背包问题都能转化成01背包。
—背包中有时可以看到一个小优化,就是w[i] \leq w[j]&&h[i] \geq h[j]是可以直接把j物品去掉,但不常用,(我没用过/哭笑),因为它不能优化最坏情况的复杂度。

代码:

memset(dp,0,sizeof(dp));
for(i=1; i<=n; i++)
    for(j=w[i]; j<=v; j++)//二维数组这里从w[i]到v,或者从v到w[i]没有区别
        dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+h[i]);

2. 01背包(一维)

处理完第i件物品时,dp[j]就表示用容量为j的背包去装前i件物品能获得的最大价值
基于上面说的二维的解法,既然处理第i件物品只与上一层(i-1)有关,那么我们就可以将二维数组转化为一维数组,转化的过程中需要考虑一个问题:怎样才能保证我们得到的结果的组成中,每件物品只参与了一次。
因为我们还是要处理n件物品,所以第一层循环不变(总不可能说物品处理的顺序会影响结果吧),然后看第二层循环,我们还是要通过第二层循环得到dp[i][j]的值(j=1、2、3、、、v),所以我们还是要遍历w[i]到v,只不过这里必须从v开始处理到w[i],因为我们处理dp[j]时会用到dp[j-w[i]]的值,而这个
dp[j-w[i]]必须是前i-1件物品参与的结果,不能有第i件物品的参与。如果我们从前往后处理,先处理dp[j-w[i]],在处理dp[j]的时候,构成dp[j-w[i]]最优解的可能也有第i件物品的参与,这样的话我们就不能保证每件物品值参与了一次。

  • O(n*v),(常用)
memset(dp,0,sizeof(dp));
for(i=1; i<=n; i++)
    for(j=v; j>=w[i]; j--)
        dp[j]=max(dp[j],dp[j-w[i]]+h[i]);

3. 完全背包

dp[j]的意义和前面一样,只不过每件物品能取无穷多次

解法:

  • 直接按完全背包的写法,O(n*v),(常用)
    因为每件物品可以不止取一次,所以要得到最优解,处理dp[j]时用到的dp[j-w[i]]就必须是第i件物品参与的结果,所以第二层循环从前往后走就行了
memset(dp,0,sizeof(dp));
for(i=1; i<=n; i++)
    for(j=w[i]; j<=v; j++)
        dp[j]=max(dp[j],dp[j-w[i]]+h[i]);
  • 转化为01背包,大于O(v * \sum (v/w[i])) > O(n * v),(后面两种解法可以先看多重背包,再回来看)
    物品个数上线为k=v/w[i]
 memset(dp,0,sizeof(dp));
 for(i=1; i<=n; i++)
     for(int k=1; k<=v/w[i]; k++)
        for(j=v; j>=w[i]; j--)
            dp[j]=max(dp[j],dp[j-w[i]]+h[i]);
  • 转化为01背包+二进制优化,O(v * \sum log(v/w[i]))>O(n*v),(常用)
memset(dp,0,sizeof(dp));
for(i=1; i<=n; i++)
    for(int k=1; k*w[i]<=v; k<<=1)
        for(j=v; j>=w[i]; j--)
            dp[j]=max(dp[j],dp[j-k*w[i]]+k*h[i]);

4. 多重背包

dp[j]的意义还是和前面一样,每件物品能取有限次

解法:

  • 转化为01背包,O(n * v * c)
    把c[i]个物品i看成c[i]个不同的物品
 memset(dp,0,sizeof(dp));
 for(i=1; i<=n; i++)
     for(int k=1; k<=c[i]; k++)
        for(j=v; j>=w[i]; j--)
            dp[j]=max(dp[j],dp[j-w[i]]+h[i]);
  • 转化为01背包+二进制优化,O(v * \sum logc[i])>O(n*v),(常用)
for(int i=1; i<=n; i++)
{
    for(int k=1; k<=c[i]; k<<=1)
    {
        for(int j=v; j>=k*w[i]; j--)
            dp[j]=max(dp[j],dp[j-k*w[i]]+k*h[i]);
        c[i]-=k;
    }
    if(c[i])
       for(int j=v; j>=c[i]*w[i]; j--)
            dp[j]=max(dp[j],dp[j-c[i]*w[i]]+c[i]*h[i]);
}

加在后面的:对每一道动态规划题目都思考其方程的意义以及如何得来,加深对动态规划的理解

扫描二维码关注公众号,回复: 8612711 查看本文章
发布了52 篇原创文章 · 获赞 26 · 访问量 3194

猜你喜欢

转载自blog.csdn.net/qq_43803508/article/details/95739832