11.30 — 12.6 关于01背包问题的初步理解

首先我有n个物品,每个物品的体积为w,价值为v,我的背包容量为bagw,问该如何装才能让背包容量最大?

动态规划的思想就是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题(最优解),最终达到解决原问题的目的。但动态规划强的在于,具有记忆性,其实也没那么高深莫测,说白了就是填表,只要根据题意找出状态转移方程,填表就完事。 通过填写表把解决过的子问题答案记录下来,新问题中遇到子问题直接提取,避免了重复计算,节约时间。

之所以叫01背包问题,就是说当我面对第i件物品时,我可以选择装(1)or 不装(0)。 我们建立一个二维数组 V[i][j] , i 是指前i个物品(包括当前物品)最佳组合对应的价值(前i个物品不一定都装),j为当前背包容量。 说通俗点,这个V[i][j]代表的含义也就是面对第i件商品,背包容量为j时所能获得的最大价值。

来,找一下递推关系式,你面对着当前这件物品,无非是 或者 不装 ,那么首先要比较一下物品体积和当前背包体积(j)
如果 j<w[i] 那没得谈,装都装不进去 所以 V(i,j)=V(i-1,j)
如果 j>=w(i) 那么我有两种选择,我可以不装也可以装,所以要比较这两种决策导致的结果,选最优,也就是 == V(i,j)=max{V(i-1,j),V(i-1,j-w(i))+v(i)}== (上一个物品可能拿出来也可能接着放在里面)

void findmax(){		//填表 
	for(int i=1;i<=n;i++){
		for(int j=1;j<=bagw;j++){
			if(j >= w[i]){
				dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
			}
			else dp[i][j] = dp[i-1][j];
		}
	} 
}

关于这个V(i-1,j-w(i))+v(i) 有点难理解,目前我也理解的也有些薄弱,可以这样理解:V[i-1][j]为装到上一个物品在背包j容量时的最佳值,那么如果我要求在j容量的时候放入现在的i物品的价值,那么是不是要先得到容量为(j-weight[i])时候的价值,即先得到 V[i-1][j-weight[i]] ,所以 V[i-1][j-weight[i]]+value[i] 为放入第i物品的价值; V[i-1][j] 就是不放入第i个物品。

填表完成后,我们只能得到最优解时候的价值,但我们不知道他具体都选了哪些物品。所以 ,回溯! 定义一个item[] 数组,初始化0,哪些放进去了哪些置1
具体回溯代码如下:

void traceback(int i,int j){	//回溯瞅瞅哪些放进去了 
	if(i>=0){
		if(dp[i][j]==dp[i-1][j]){
			item[i] = 0;
			traceback(i-1,j);
		}
		else if(j-w[i]>=0 && dp[i][j]==dp[i-1][j-w[i]]+v[i]){
			item[i] = 1;
			traceback[i-1,j-w[i]];
		}
	}
}

主要思想是寻找状态转移方程然后填表,之后回溯找选中的解,嗯…具体代码如下:

#include<iostream>
#include<algorithm>
using namespace std;

int n;	//我有n个物品 
int v[150]={0};	//记录我这n个物品每个的价值
int w[150]={0};//记录我这n个物品的体积
int bagw;	//记录背包大小
int dp[150][150]={0};//动态规划表 
int item[150]={0};//记录最终放入背包了哪些物品 

void findmax(){		//填表 
	for(int i=1;i<=n;i++){
		for(int j=1;j<=bagw;j++){
			if(j >= w[i]){
				dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
			}
			else dp[i][j] = dp[i-1][j];
		}
	} 
}

void traceback(int i,int j){	//回溯瞅瞅哪些放进去了 
	if(i>=0){
		if(dp[i][j]==dp[i-1][j]){
			item[i] = 0;
			traceback(i-1,j);
		}
		else if(j-w[i]>=0 && dp[i][j]==dp[i-1][j-w[i]]+v[i]){
			item[i] = 1;
			traceback[i-1,j-w[i]];
		}
	}
}

void print(){
	for(int i=0;i<n;i++){
		for(int j=0;j<bagw;j++){
			cout<<dp[i][j]<<' ';
		}
		cout<<endl;
	}
	cout<<endl;
	for(int i=0;i<n;i++){
		cout<<item[i]<<' ';
	}
	cout<<endl;
}

int main(){
	cin>>n;
	cin>>bagw;
	for(int i=1;i<=n;i++){
		cin>>v[i];
	}
	for(int j=1;j<=n;j++){
		cin>>w[j];
	}
	findmax();
	traceback(n,bagw);
	print();
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值