背包问题都涉及到动态规划,利用dp进行更加优化的计算。

一、01背包

最基本的是01背包问题,题目一般类似:“在一定数目物品内,挑选总重量不超过一定数目的物品,其中每个物品只能选一次,求背包内物品价值的最大值或者最小值”,从名字就可以看出,要么选0个,要么选1个。

如果按照暴力的方法,时间复杂度会爆表,这里采用的是记忆化搜索的方式,加以DP。

首先,选一个二维数组dp,这个数组的含义是从前i个物品中选出总重量不超过j的物品时总价值的最大值,也就是说,对于数组中的任意一个元素dp[i][j],其含义为在前i个物品中选出总重量不超过j的物品的最大值。

定义好了数组,接下来就是找状态转移方程。最容易找到的就是最初的状态,即dp[0][j]=0,在一个物品也不选的时候,不论总质量是多少,总价值肯定是0。接下来的状态都是在这个基础状态上进行扩展。既然是放物品,那么肯定是物品一个一个遍历,如果能放不下,其最大价值肯定还等于上一个状态,也就是说当j<w[i]时,dp[i+1][j]=dp[i][j],如果能放下,就需要比较是放下这个物品价值大还是不放价值大,当选择放的时候,相当于在前i个物品中,空间为当前空间减去w[i+1]的情况再放这个物品,比较这个情况下价值的大小,即当j>=w[i]时,dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]),这样所有的状态我们就找到了,即状态转移方程为:
dp[0][j]=0;
dp[i+1][j]=dp[i][j] i<w[i];
dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]) j>=w[i]

根据这个就可以写出01背包最大值问题的模板了

for(int i=0;i<N;i++)
{for(int j=0;j<=wei;j++){if(j<w[i])dp[i+1][j]=dp[i][j];elsedp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);}
}
cout<<dp[N][wei]<<endl;

在这个的基础上可以进行空间复杂度上的优化,不难发现每次对下一行的数据的更新都是取决于上一行的数据,所以并不一定需要开一个二维数组,一维数组就可以解决问题,我们关心的是最后一次外循环的值,中间过程我们并不需要知道,所以可以进行空间上的优化。

二维数组中我们进行的操作实际上就是在每个物品的基础上,遍历所有可能的背包空间,如果放得下去就比较放与不放对价值的影响,转换为一维的时候,其实比较的就是在上一次操作的情况下,哪些还可以放得下这个物品,放得下的就进行比较。所以也就是要对w[i]到wei的范围进行更新,转换为代码就是下面这个形式,转换后dp数组的含义就变成了容量不超过j时的最大价值了,省略了前几个物品这一项。

for(int i=0;i<N;i++)
{for(int j=wei;j>=w[i];j--){dp[j]=max(dp[j],dp[j-w[i]]+pri[i]);}
}
cout<<dp[wei]<<endl;

这里还需要注意,01背包的一维数组表示,更新的时候是逆序的,从代码不难看出,对一个值的更新是基于这个值左边的值进行更新的,如果是顺序更新,那么在更新时一定会先更新左边的值,从而使得后更新的值有可能基础被更新过了,而逆序更新就可以避免这个问题。也可以这么说,因为01背包里面每个物品只能放一次,如果顺序更新,那么假设状态1已经放了一个物品了,那么基于状态1的状态2如果需要更新,那么不就变成了放两个物品了么,这不就出错了,所以应该需要逆序更新。

这里举例子都是举的求最大值,这时需要把数组初始化为0,之后每一次都选取最大值即可。而当要求的是最小值时,则应该把数组初始化为很大的数,每次选取最小值。dp[N][wei]就是要求的最值。

二、完全背包

完全背包问题是在01背包问题上进行的延伸,01背包每次物品的数目只能是0或者1,而完全背包问题就脱离了这个限制,题目一般类似:“在一定数目物品内,挑选总重量不超过一定数目的物品,其中每个物品可以选多次,求背包内物品价值的最大值或者最小值”。这里就需要对上面的模板进行一定的修改了。

其实最笨的办法是在01背包的基础上再增加一层循环,用于记录增加的件数

for(int i=0;i<N;i++)for(int j=0;j<=wei;j++)for(int k=1;k*wei[i]<=j;k++)dp[i+1][j]=max(dp[i][j],dp[i][j-k*wei[i]]+k*val[i]);
cout<<dp[N][wei]<<endl;

这种方法比较好懂,但是时间复杂度太大,需要进行优化,这里直接在01背包一维的基础上进行一个优化,其实这两种背包问题都是相通的,区别就在于01不可以放多个而完全背包可以放多个,在前面讲一维化的地方说过,之所以逆序更新是防止放两个的情况出现,那么这里完全背包就正好利用了这一点,把01背包的逆序更新换成顺序更新,就可以解决放几个的问题了,把所有还可以放的状态都更新一遍,从左向右进行更新,如果前面的状态就已经放得下一个了,那么基于这个状态的另一个状态还可以再放一个,所以就基于上个状态继续更新,因而是正序更新。具体表示为,当背包空间为j时,最大价值等于当前值和加上一个当前物品增加价值之后中更大的值。代码如下

for(int i=0;i<N;i++)
{for(int j=w[i];j<=wei;j++){dp[j]=max(dp[j],dp[j-w[i]]+pri[i]);}
}
cout<<dp[wei]<<endl;

所以在记忆这两种背包问题的时候,只需要理解好更新顺序的问题,模板都是一样的,只需要根据顺序进行修改就好了。

三、分组背包

分组背包问题是在01背包的基础上又进行了延伸,01背包问题中每个物品要么选要么不选,在分组背包中,将一系列物品分成几组,一次在一个小组中选择一个或者不选,其实01背包是一种特殊情况下的分组背包,即每个组只有一个物品时的分组背包,我们依然可以沿用01背包的解题思路,只不过进行一下加工,这里直接取一维情况的dp数组,其实我们可以这样认为,每个组既然只能选一个,我们可以先限定一个组别,之后在这个组别中,在总重量允许的条件下,看当前重量下是不是放得下这个组中的物品,放得下就选取价值的更大值。由于每次选择的都是最大值而且是在这一个组中进行的操作,所以保证了一个组中只选择了一个物品。由于分组背包是01背包延伸来的,所以依然是用逆序遍历来实现一维数组的dp。

模板类似下面的代码

for(int i=1;i<=N;i++)//第几组for(int j=M;j>=1;j--)//允许的重量 for(int k=1;k<=这组的物品数;k++)//每组选物品 if(物品的重量<=j)dp[j]=max(dp[j],dp[j-物品的重量]+val[i][k]);

四、多重背包

多重背包问题的思路跟完全背包的思路非常类似,只是每种物品的取值是有限制的,因为每件物品的数量是有限制的。

多重背包一般采取转化为01背包的方法,将每个物品按照2的幂次分成几个物品,这样就变成了01背包。把第i种物品换成n[i]件01背包中的物品。考虑二进制的思想,考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略——取0…n[i]件——均能等价于取若干件代换以后的物品。另外,取超过n[i]件的策略必不能出现。分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。这里一般都会用到二进制优化来减少时间复杂度,不然的话三层循环暴力确实有些吃不消。

模板如下

for(int j=1;j<=num[i];j*=2)//二进制优化
{for(int k=n;k>=j*i;k--)//转换为01背包,所以需要逆序更新 {if(dp[k-i*j]==1)dp[k]=1;}num[i]-=j;//剩余数量进行更新
}
if(num[i]*i!=0)//对于不能进行二进制更新的部分直接当做一个物品处理
{for(int k=n;k>=num[i]*i;k--)//对于剩下的部分应该也遍历一遍 {if(dp[k-num[i]*i])dp[k]=1;}
}

五、总结

总的来说,这四类背包问题的关系大致如下:
01背包:每个物品只有一个,要么选要么不选
完全背包:在01背包基础上解除了只有一个的限制,每个物品随便选
分组背包:嵌套的01背包,每组要么选一个要么不选,每组里面的物品只能选一个,也可以理解为最大数量为1的要么选要么不选
多重背包:在完全背包上加了数量的限制,依然是随便选,但不能超过限制。

结题思路
01背包:逆序更新
完全背包:顺序更新
这两个是基础,理解好原理就不难区分
分组背包:01背包的基础上,再套一层循环来检验每一组的物品
多重背包:二进制优化后转化为01背包

完全背包问题+01背包问题+分组背包+多重背包 总结相关推荐

  1. 动态规划总结(01背包 完全背包 多重背包)

    动态规划总结(01背包 完全背包 多重背包) 一.学习资料 1.UVA DP 入门专题 2.夜深人静写算法(二) - 动态规划 3.算法之动态规划 4.什么是动态规划?动态规划的意义是什么? 5.01 ...

  2. 经典背包问题 01背包+完全背包+多重背包

    01 背包 有n 种不同的物品,每个物品有两个属性,size 体积,value 价值,现在给一个容量为 w 的背包,问最多可带走多少价值的物品. int f[w+1]; //f[x] 表示背包容量为x ...

  3. 背包问题——01背包/完全背包/多重背包

    背包问题是典型的动态规划问题,本文将对典型的背包问题进行总结. 0-1背包问题 有 N 件物品和一个容量是 V 的背包.每件物品只能使用一次. 第 i 件物品的体积是 vi,价值是 wi. 求解将哪些 ...

  4. 动态规划之背包问题---01背包---完全背包---多重背包

    本篇博客是基于Carl大佬的刷题笔记 (代码随想录) 进行总结的 另外加入了我自己的一些整理,特此记录,以防遗忘 几种在面试中常见的背包,其关系如下: 通过这个图,可以很清晰分清这几种常见背包之间的关 ...

  5. 【python】一篇讲透背包问题(01背包 完全背包 多重背包 二维费用背包)

    面对背包问题,有一个很重要的方程式:状态转移方程式 所以每一种背包问题我都会给出状态转移方程式 #01背包 什么是01背包型问题? 先给大家感受一下01背包型问题: 给定n种物品和一背包.物品i的重量 ...

  6. 背包——多重背包(hdu2191)

    首先,如果对01背包不理解的同学请移步  http://blog.csdn.net/sm9sun/article/details/53235986 理解了01背包之后,我们来说明一下多重背包,多重背包 ...

  7. 背包问题-01背包问题

    n个物品,总体积是V,每个物品的体积的vi,每个物品的最大价值是wi,在不超过V的体积下求最大价值 eg 背包容积为 5 物品数量为 4 物品的体积分别为 {0, 1, 2, 3, 4} 物品的价值分 ...

  8. POJ 3260 The Fewest Coins 最少硬币个数(完全背包+多重背包,混合型)

    题意:FJ身上有各种硬币,但是要买m元的东西,想用最少的硬币个数去买,且找回的硬币数量也是最少(老板会按照最少的量自动找钱),即掏出的硬币和收到的硬币个数最少. 思路:老板会自动找钱,且按最少的找,硬 ...

  9. 洛谷P2851 [USACO06DEC]最少的硬币The Fewest Coins(完全背包+多重背包)

    题目描述 Farmer John has gone to town to buy some farm supplies. Being a very efficient man, he always p ...

最新文章

  1. GAN(Generative Adversarial Nets)研究进展
  2. 第十六周程序阅读(5)
  3. WPF ,listbox,平滑滚动的2种方式。
  4. python-三元运算
  5. pmp每日三题(2022年3月4日)
  6. win8下cocos2dx-3.2+VS2012环境配置及项目创建
  7. NOIP模拟测试38「金·斯诺·赤」
  8. c#求三角形面积周长公式_C#源代码—三角形面积、圆的面积
  9. android布局参照示例_约束布局Android示例–第2部分
  10. 基于yolo的口罩识别(开源代码和数据集)
  11. 两个类似的ViewModel一个可以重写事件,另一个不能重写事件,是哪里出了错。...
  12. Java数组去掉重复的方法集
  13. StyleWriter英文润色软件使用说明(含破解安装包)
  14. 2019年美赛建模D题解题思路
  15. Codeblock汉化教程
  16. 浏览器清除缓存快捷键
  17. 笔记本通过网线连接台式机共享网络
  18. java readable_Java-Readable
  19. 【CSS 颜色的 合法颜色值 (详细介绍)】
  20. Redis的数据结构及底层原理

热门文章

  1. golang中如何使用http,socket5代理
  2. zjoi 2008 树的统计——树链剖分
  3. LA 3989 - Ladies' Choice 稳定婚姻问题
  4. Enterprise Libary 2 Hands-on-Lab 发布了
  5. python第六篇文件处理类型
  6. 数据库优化之mysql【转】
  7. 【转】Cron表达式简介
  8. 团队作业4——第一次项目冲刺(Alpha版本)4.25
  9. 怎样区分直连串口线和交叉串口线?
  10. Beagle 0.2.16(附 Ubuntu Edgy 安顿源)