问题描述
有n个重量分别为w1、w2、... 、wn的物品(物品编号为1~n),它们的价值分别为v1、v2、... 、vn,给定一个容量为W的背包。设计从这些物品中选取一部分物品放入该背包的方案,每个物品要么选中要么不选中,要求选中的物品不仅能够放入背包中,而且具有最大的价值。
问题分析
- 一个物品 选 或 不选 ,不能选择部分放入
- 选中的物品重量之和 < W
- 选中的物品价值 最大
举例说明
设 W=10,n=5
物品编号 | 重量 | 价值 |
---|---|---|
1 | 2 | 6 |
2 | 2 | 3 |
3 | 6 | 5 |
4 | 5 | 4 |
5 | 4 | 6 |
问题求解
我们设一个x数组,用来表示是否选择该物品,例:x1=1,选中1号物品放入背包,背包剩余重量=W-w1;若x1=0,则不选择1号物品,即在2~n号物品中继续选择,背包剩余重量仍为W。因此,在处理问题时出现两种状态:
- 背包中不放入物品i,则=0,背包不增加重量和价值,且剩余重量r不变
- 背包中放入物品i,则=1,背包增加重量和价值,剩余重量 r=W-
设置一个动态规划数组dp,dp[i ][r]表示背包剩余容量为r(1 r W),已考虑物品1、2、... 、i(1 i n)时背包装入物品的最优价值。对应的状态转移方程如下:
- dp[i][0] = 0 (背包的重量为0,即没容量存放物品,所以物品总价值为0)【边界条件dp[i][0]=0( 1 i n)】
- dp[0][r] = 0 (没有物品可以放入背包,所以物品总价值为0)【边界条件dp[0][r]=0( 1 r W)】
- dp[i][r] = dp[i-1][r] 【当 r < w[i] 时(物品i放不下)】例:若背包剩余重量r=2,物品i的重量w[i]=3,则物品i不能放入背包中,所以背包在物品i的情况下的总价值仍然等于背包在物品i-1的情况下的总价值,即背包的剩余重量r及总价值没有改变。(相当于⬆️处理问题时的第一种状态)
- dp[i][r] = max( dp[i-1][r] , dp[i-1][r-w[i]]+v[i] ) 【选择 放入物品i :dp[i-1][r-w[i]]+v[i] 和 不放入物品i :dp[i-1][r] 两者中价值较大的一种】 (相当于⬆️处理问题时的第二种状态)
因此,dp[n][W]便是该问题的最优解 。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
2 | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 9 | 9 | 9 | 9 |
3 | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 9 | 11 | 11 | 14 |
4 | 0 | 0 | 6 | 6 | 9 | 9 | 9 | 10 | 11 | 13 | 14 |
5 | 0 | 0 | 6 | 6 | 9 | 9 | 12 | 12 | 15 | 15 | 15 |
回推最优解过程:
- i=5,r=W=10,dp[5][10] > dp[4][10],则x[5]=1,r=r-w[5]=6
- i=i-1=4,dp[4][6] = dp[3][6],则x[4]=0
- i=i-1=3,dp[3][6] = dp[2][6],则x[3]=0
- i=i-1=2,dp[2][6] > dp[1][6],则x[2]=1,r=r-w[2]=4
- i=i-1=1,dp[1][4] > dp[0][4],则x[1]=1,r=r-w[1]=2
所以,最终装入背包的物品为1、2、5,重量为8,总价值为15。
代码展示
// 0/1背包问题 动态规划
#include <iostream>
#include <iomanip>
#define N 100
using namespace std;
int W; //背包总重量
int weight[N]; //物体重量
int value[N]; //物体价值
int dp[N][N]; //背包剩余容量
bool x[N]; //记录是否选取该物品,初始值为0
void Solve(int n)
{
for(int i=0; i<=n; i++)
dp[i][0] = 0; //背包重量为0时,无物品可放
for(int i=0; i<=W; i++)
dp[0][i] = 0; //没有物品时,背包可放重量很多也没用
for(int i=1; i<=n; i++){
for(int j=1; j<=W; j++){
if(j<weight[i]) dp[i][j] = dp[i-1][j];
else dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
}
}
}
int main()
{
cout<<"Input the weight of the backpack:";
cin>>W;
int n;
cout<<"Input number of objects:";
cin>>n;
cout<<"Input the weight and value of the object:"<<endl;
for(int i=1; i<=n; i++)
cin>>weight[i]>>value[i];
Solve(n);
int temp=W;
int sum=0;
for(int i=n; i>0; i--){
if(dp[i][temp] != dp[i-1][temp]){
x[i] = true;
temp -= weight[i];
sum += value[i];
}
}
cout<<"The selected objects are: ";
for(int i=0; i<=n; i++)
if(x[i]) cout<<i<<" ";
cout<<endl<<"The total values are: "<<sum<<endl;
return 0;
}
运行结果
参考资料:《算法设计与分析》第2版