问题情景:给你一个背包,告诉你其最大体积V,然后有一系列物品,每一个物品都有其对应的价值和体积。每个物品只能装一次(只有一个)就会有两种问法:
1.在不超过背包体积的情况下(不一定非要装满)背包物品最大价值总和?
2.在恰好装满背包的情况下,包内最大价值和?也有可能无论怎么装都无法恰好装满,返回-1,0等一些标记值。
我们接下来推一下状态表示和转移方程
如果,dp[i]表示从前i个物品中选取不超过背包体积的最大价值,我们发现这个dp数组就和体积变量无关了。而我们每装一个物品其背包剩余体积都会变,因此一维数组无法满足我们的表示需求。
改进:dp[i][j]表示在不超过j体积的情况下从前i个物品中选取最大总价值。(对于问题1的状态表示)
而对于第二种问题,dp[i][j]表示从前i个物品中选恰好能凑出j个体积的最大总价值(也有可能组不出)
我们先推导问题1的状态表示方程:
到第i个物品时,我们也可以选择选或不选,然后在这两种情况取最大值即可。
当不选时,那和从前i-1个选法是一样的。
当选时,首先要判断第i个物品体积是否大于j,如果大于了,只选第i个都已经过大了,显然不满足要求,因此我们要加个判断条件,在此条件下,它就等于从前i-1个物品中选一个体积不超过j-v[i]的价值加第i个物品价值(并不是看前一个元素)。(v数组表示每个物品的体积,w数组表示每个物品的价值)

在数组初始化方面,我们采取多一行一列表示,这样数组下标就能和物品序号对应了(第1,第2个物品。。。。)
第一行我们初始化为0(第一行表示前0个物品找出不超过j的最大价值,都没有物品,价值肯定都是0)
第一列我们也是都初始化为0(选出一个不超过0的总价值,肯定什么都选不了)
最终结果我们取dp[n][V]即可(题意中的在n个物品中选体积不超过V背包的总价值)
接下来我们来看第二个问题:
状态转移方程与第一个几乎相同。
首先不一样的是,我们会遇到无论怎么装都装不满的情况,这就不需要谈最大价值了,这种情况我们统一赋值为-1(人为规定)。
而当不选第i个物品,那么和dp[i-1][j]结果是相同的
如果选第i个,除了考虑是否v[i]<=j,还要满足dp[i-1][j-v[i]]不等于-1(即满足要求恰好装满)这时候背包恰好剩下v[i]个位置直接放入即可。
依旧是取二者情况最大值。
初始化方面,第一行除了第一个位置我们为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-v[i]一定是在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循环的判断条件的解释
如果其不满足条件,直接就是上一次的值(dp[i-1][j])映射了我们的二维时的思想。