动态规划算法经典问题——01背包问题

问题情景:给你一个背包,告诉你其最大体积V,然后有一系列物品,每一个物品都有其对应的价值和体积。每个物品只能装一次(只有一个)就会有两种问法:

1.在不超过背包体积的情况下(不一定非要装满)背包物品最大价值总和?

2.在恰好装满背包的情况下,包内最大价值和?也有可能无论怎么装都无法恰好装满,返回-1,0等一些标记值。

我们接下来推一下状态表示和转移方程

如果,dpi表示从前i个物品中选取不超过背包体积的最大价值,我们发现这个dp数组就和体积变量无关了。而我们每装一个物品其背包剩余体积都会变,因此一维数组无法满足我们的表示需求。

改进:dpij表示在不超过j体积的情况下从前i个物品中选取最大总价值。(对于问题1的状态表示)

而对于第二种问题,dpij表示从前i个物品中选恰好能凑出j个体积的最大总价值(也有可能组不出)

我们先推导问题1的状态表示方程:

到第i个物品时,我们也可以选择选或不选,然后在这两种情况取最大值即可。

当不选时,那和从前i-1个选法是一样的。

当选时,首先要判断第i个物品体积是否大于j,如果大于了,只选第i个都已经过大了,显然不满足要求,因此我们要加个判断条件,在此条件下,它就等于从前i-1个物品中选一个体积不超过j-vi的价值加第i个物品价值(并不是看前一个元素)。(v数组表示每个物品的体积,w数组表示每个物品的价值)

在数组初始化方面,我们采取多一行一列表示,这样数组下标就能和物品序号对应了(第1,第2个物品。。。。)

第一行我们初始化为0(第一行表示前0个物品找出不超过j的最大价值,都没有物品,价值肯定都是0)

第一列我们也是都初始化为0(选出一个不超过0的总价值,肯定什么都选不了)

最终结果我们取dpnV即可(题意中的在n个物品中选体积不超过V背包的总价值)

接下来我们来看第二个问题:

状态转移方程与第一个几乎相同。

首先不一样的是,我们会遇到无论怎么装都装不满的情况,这就不需要谈最大价值了,这种情况我们统一赋值为-1(人为规定)。

而当不选第i个物品,那么和dpi-1j结果是相同的

如果选第i个,除了考虑是否vi<=j,还要满足dpi-1j-v\[i]不等于-1(即满足要求恰好装满)这时候背包恰好剩下vi个位置直接放入即可。

依旧是取二者情况最大值。

初始化方面,第一行除了第一个位置我们为0剩下都是-1,因为我选第0个物品的情况下(即不选)我是无法把你的背包装满的,但是第一个位置背包体积为0,某种意义上也满足要求,但价值是0.

第一列我们初始化为0.

接下来我们写一下二种问题的模板:

cpp 复制代码
//假设v[N],w[N]已经赋值
// int dp[N];
// int n ,V;
cin>>n>>v; 代表物品个数和背包体积


//第一种问题
for(int i=1;i<=n;i++)
  for(int j=1;j<=V;j++)
    {
      dp[i][j]=dp[i-1][j];
      if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
    }

return dp[n][V];

//第二种问题

for(int j=1;j<=V;j++) dp[0][j]=-1;


for(int i=1;i<=n;i++)
  for(int j=1;j<=V;j++)
    {
      dp[i][j]=dp[i-1][j];
      if(j>=v[i]&&dp[i-1][j-v[i]]!=-1) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
    }

return dp[n][V]==-1?0:dp[n][V]

代码优化:滚动数组思想

我们也可以不用二维数组,用一维代替二维,即当填完第一行后,第二行根据第一行的数据进行赋值(把上一次的赋值覆盖,反正我们要的是最后一行的数据)

填表顺序就要从右向左填了,因为j-vi一定是在j的左边,如果我们从左往右可能就找不到上次的值了。

cpp 复制代码
//假设v[N],w[N]已经赋值
// int dp[N];
// int n ,V;
cin>>n>>v; 代表物品个数和背包体积


//第一种问题
for(int i=1;i<=n;i++)
  for(int j=V;j>=v[i];j--)
    {
      dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
    }

return dp[V];

//第二种问题

for(int j=1;j<=V;j++) dp[0][j]=-1;


for(int i=1;i<=n;i++)
  for(int j=V;j>=v[i];j--)
    {
      if(dp[j-v[i]]!=-1) dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
    }

return dp[V]==-1?0:dp[V]

关于内层for循环的判断条件的解释

如果其不满足条件,直接就是上一次的值(dpi-1j)映射了我们的二维时的思想。

相关推荐
倒头就睡的小比特5 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!5 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼5 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光5 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark5 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her15 天前
并查集-听课笔记
笔记·算法·并查集
码流子5 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven5 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark5 天前
从算法设计模式看编程思维的抽象能力4
算法
2601_962218615 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法