问题描述
🚩有N个物品,其重量分别为weight[i],其价值分别为value[i],现有一个容量为V的背包,问怎样装商品才能使得背包中的价值最大?
int N=5,V=10;
int weight[]={0,3,6,3,8,6};
int value[]={0,4,6,6,12,10};
思路讲解
可先看B站这个视频讲解:https://www.bilibili.com/video/BV1K4411X766/
其实就是相当于我们创建一个表格即dp[][],左边那一列表示商品编号,上面那一行表示背包容量,表格内容是此时状态的最大价值
先看结果,如下
解释:
一行一行的填写,从上至下从左至右。如编号1重量为3,那么容量为0、1、2时都不能放下,最大价值都为0
当背包重量为3时可以放下商品1所以最大价值为4
再考虑第二行,j等于0~2时都放不下最大价值为0,由于商品2重量为6,那么背包容量为3~5时都放不下商品2只能放商品1最大价值为商品1的价值4。
而当背包容量为6时,此时能够放下商品2,那么就要考虑要不要放尚品2
1️⃣放商品2,那么最大价值就是此时背包的容量减去商品2的重量为0,再看第一行重量为0时的价值也是0,那么最大价值为6
2️⃣不放商品2,那么最大价值就是第一行背包容量为6时的最大价值为4
放商品2最大价值为6,不放最大价值为4,我们选最大值为6
总结如下:
若背包容量小于商品n的重量放不下商品
- 那么前n个商品的最大价值和前n-1个商品的最大价值是一样的(既然不放相当于就不考虑这个商品,那么考虑前n个商品其实就是考虑前n-1个商品)
若此时背包装得下商品n
- 若选择放商品n,那么背包可用容量KV就等于背包总容量SV减去商品n的重量nV,那么问题就变成了只考虑前n-1个商品且背包容量为KV时的最大价值,而此前我们已经求得了这个值填写在了表中所以直接用这个值加上商品n的价值就是最大价值
- 若不放商品n,那么前n个商品的最大价值和前n-1个商品的最大价值是一样的
- 从以上两个选择选一个最大的就是最终的最大价值
对应的代码如下
for (int n = 1; n <= N; n++) //遍历商品1~N
{
for (int v = 0; v <= V; v++) //遍历容量0~V
{
if(v>=weight[n]) //背包容量大于等于商品n的容量时
{
dp[n][v]=max(dp[n-1][v-weight[n]]+value[n],dp[n-1][v]); //选择放商品n和不放商品n哪个价值最大
}
else //背包容量小于商品n的容量时
{
dp[n][v]=dp[n-1][v]; //此时商品n一定不能放,此时最大价值就是商品1到i-1中,容量为v时的最大价值
}
}
}
而我们怎么知道求得的最大价值其中我们选了哪些商品呢
其实就是从表的右下角开始回溯,如果dp[n][j]和dp[n-1][j]值是一样的,那么我们肯定没装商品i(相当于我们就没考虑商品n)。事实上,我们推dp[n][j]时如果选择不放商品n那么我们就让dp[n][j]=dp[n-1][j]
所以相当于我们根据两个数值是否相等来判断当时我们是否选了商品n
相当于是求最大价值的一个逆过程
如果装了商品n,那么用总容量减去商品n的容量得到可用容量kV再来判断是否放了商品n-1,其实也就是看dp[n-1][kv]和dp[n-2][kv]是否相等,以此类推
int n=N,v=V;
while(n!=0)
{
if(dp[n][v]==dp[n-1][v])
{
cout<<"商品"<<n<<"未被选中"<<endl;
}
else
{
cout<<"商品"<<n<<"被选中"<<endl;
v=v-weight[n];
}
n--;
}
🦄完整代码如下
#include<bits/stdc++.h>
using namespace std;
int dp[15][15];
int N=5,V=10;
int weight[]={0,3,6,3,8,6};
int value[]={0,4,6,6,12,10};
//打印初始状态
void printInit()
{
cout<<"共"<<N<<"个商品,"<<"背包容量为"<<V<<endl;
for (int i = 1; i <= N; i++)
{
cout<<"商品"<<i<<"的重量为"<<weight[i]<<",价值为"<<value[i]<<endl;
}
}
//求最大价值
int maxValue()
{
for (int n = 1; n <= N; n++) //遍历商品1~N
{
for (int v = 0; v <= V; v++) //遍历容量0~V
{
if(v>=weight[n]) //背包容量大于等于商品n的容量时
{
dp[n][v]=max(dp[n-1][v-weight[n]]+value[n],dp[n-1][v]); //选择放商品n和不放商品n哪个价值最大
}
else //背包容量小于商品i的容量时
{
dp[n][v]=dp[n-1][v]; //此时商品n一定不能放,此时最大价值就是商品1到n-1中,容量为v时的最大价值
}
}
}
return dp[N][V];
}
//打印最大价值的商品选择情况
void printSelect()
{
int n=N,v=V;
while(n!=0)
{
if(dp[n][v]==dp[n-1][v])
{
cout<<"商品"<<n<<"未被选中"<<endl;
}
else
{
cout<<"商品"<<n<<"被选中"<<endl;
v=v-weight[n];
}
n--;
}
}
int main()
{
printInit();
cout<<"最大价值为:"<<maxValue()<<endl;
printSelect();
return 0;
}