【数据结构与算法】01背包问题及输出具体方案

文章目录

  • 背景
  • 让我们看下具体问题
  • 解题思路
  • 代码实现
  • 如何得到具体的方案

背景

最近重新复习下动态规划相关知识,所以把经典的背包问题拿出来重新看下。最为经典的莫过于背包九讲,详见:
这里只是把自己在做的过程中一些想法记录下来。

本文主要描述01背包问题。背包问题指的是我们有多少件物品要放进背包,求放进背包的价值最大。而01背包指的是每个种类的物品只有1件。

让我们看下具体问题

现在有三件物品,笔记本、手机跟手表。每件物品重量跟价值如下:

物品 笔记本 手机 手表
重量 3kg 1kg 1kg
价值 2000 5000 3000

现有一个4kg的背包,请问要怎么分配空间,使得整体价值最大。

解题思路

动态规划的思路实际就是把大的问题拆分成小的问题,
这里我们先定义状态:dp[i][j],把前i个物品放到容量为j的背包里的最大价值。
那么有:
1)如果第i件物品,我们选择放到背包里(背包有足够容量j-weight[i]>0):
dp[i][j] = max(dp[i-1][j] , dp[i-1][j-weight[i]] + value[i])
2)不放到背包里:
dp[i][j] = dp[i-1][j]
按照这个思路,遍历 i:1-3,j:1-4
i=0,j=0~4,这是不管背包多少重量,都没有价值,所以dp[0][j] = 0。这也是dp[][]的初始状态,相当于这里进行了初始化。
i=1,这时我们看下笔记本时,背包重量从1到4的时候,背包里面的最大价值会是多少。
i=1,j=1或2,背包无法放下笔记本,dp[1][1] = dp[1][2] = 0
i=1,j=3,背包能放下笔记本,dp[1][3] = dp[0][3-1] + value[0] = 2000
同理,i=1,j=4时,dp[1][4] = dp[0][4-1] + value[0] = 2000
那么我们得到:

1 2 3 4
笔记本 0 0 2000 2000
手机
手表

i=2,这时我们手里有笔记本及手机,背包重量从1到4,背包里面最大价值会是多少。
i=2,j=1时,背包无法放下笔记本,只能放下手机,这时dp[2][1] = dp[1][1-1] +value[2] = 5000
同理,i=2,j=2时, dp[2][2] = dp[1][2-1] +value[2] = 5000
i=2,j=3时, 背包可以放下笔记本或者手机,但无法同时存放,那么我们要对比具体价值大小: dp[2][3] = max(dp[1][3],dp[1][3-1] +value[2]) = max(2000,5000),得到只能存放手机
i=2,j=4时,背包可以放下笔记本和手机。
这时的表格变成:

1 2 3 4
笔记本 0 0 2000 2000
手机 5000 5000 5000 7000
手表

i=3,这时我们手里有笔记本及手机、手表,背包重量从1到4,背包里面最大价值会是多少。
i=3,j=1时,背包无法放下笔记本,只能放下手机或手表,这时dp[3][1] =max(dp[2][1],dp[2][0]+value[3])=max(5000,3000) = 5000
这里成立的原因是,我们已经得到了dp[2][1],这个值已经被更新为前2个物品在1kg背包时的最大价值,再加上第三个对比即可。
因此,我们也可以得到其他情况:

1 2 3 4
笔记本 0 0 2000 2000
手机 5000 5000 5000 7000
手表 5000 8000 8000 8000

代码实现

#include 
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
#define MAX 10
int getMax(int x,int y){
	return x>y?x:y;
}
int getValue(int m,int n,int *weight,int *value){

	int dp[MAX][MAX];
	for(int i=0;i<=m;i++){
		for(int j=0;j<=n;j++){
			dp[i][j]=0;
		}
	}
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			if(j>=weight[i-1]){
				dp[i][j]=getMax( dp[i-1][j],dp[i-1][j-weight[i-1]]+value[i-1] );		//这里要注意我们取得都是weight[i-1] value[i-1] 因为这里的数据是按0开始存放。
			}else{
				dp[i][j] = dp[i-1][j];
			}
		}
	}
	int maxValue = dp[m][n];
	
	return maxValue; 
}


int main(int argc, char** argv) {
	
	
	int m = 3;
	int n = 4;
	int weight[]={3,1,1};
	int value[]={2000,5000,3000};
	
	int res = getValue(m,n,weight,value);
	
	cout<<"value:"<

运行得到:
value:8000

如何得到具体的方案

按照最大价值的思路,做个逆向运算即可。即:
我们得到了整个表dp[][],现在我们遍历所有的物品i,看下是否有选中具体的某一件。
如何判断有没有选中物品i,根据我们上面的条件,如果选择放某一件物品i,具体的dp会更新如下:
dp[i][j] = dp[i-1][j-weight[i]] + value[i]
那么判断上述这个等式如果成立的话,也就是选中了i,
编写对应代码验证看看:

	int restj = n;
	for(int i=m;i>0;i--){
		for(int j=restj;j>0;j--){
			if(dp[i][j]==dp[i-1][j-weight[i-1]]+value[i-1]){
				restj -= weight[i-1];
				cout<< "pick:"<

运行得到:
pick:3 restj:3
pick:2 restj:2

你可能感兴趣的:(【编程语言】,c++,动态规划,背包问题,01背包,软件算法)