0-1背包问题(5种方法)

大家好,我是一只学弱狗,记录学习的点点滴滴!

优质文章

优质专栏


题目描述
  有n件物品要装入背包,第i件物品的重量wi,价值vi,i=1,2,…,n,背包最多允许装入的重量为B,问如何选择装入背包的物品,使得总价值达到最大?

穷举法
  解是一个取值只能是0或1的向量,最先考虑考虑的是穷举,即遍历出所有的情况,然后根据约束条件对所有情况进行筛选,从符合条件的情况中选择最优解。

#include <iostream>
using namespace std;

//最大价值 
int maxv = -1;
void dfs(int i,int *recode,int n,int *w,int *v,int B,int *result){
	if(i==n){//得到一个解 
		int cv=0;//当前解的价值 
		int cw=0;//当前解的重量 
		for(int i=0;i<n;i++){
			cw += recode[i]*w[i];
			cv += recode[i]*v[i];
		}
		if(cw<=B && cv>maxv){//如果当前解的重量小于等于背包重量且价值大于之前的,就替换 
			maxv = cv;
			for(int i=0;i<n;i++) result[i]=recode[i];//更新解 
		}
		return ;
	}
	recode[i]=0;//当前商品不放 
	dfs(i+1,recode,n,w,v,B,result);
	recode[i]=1;//当前商品放 
	dfs(i+1,recode,n,w,v,B,result);
} 

int main(){
	freopen("背包问题.txt","r",stdin);
	int n,B;//初始化 
	cin>>n>>B;
	int *w = new int[n];
	int *v = new int[n];
	for(int i=0;i<n;i++) cin>>w[i];
	for(int i=0;i<n;i++) cin>>v[i];
	int * recode = new int[n];//存放临时的解状态 
	int * result = new int[n];//存放最优方案 
	
	dfs(0,recode,n,w,v,B,result);
	
	cout<<maxv<<endl;
	for(int i=0;i<n;i++) cout<<result[i]<<" "; cout<<endl;
	return 0;
} 

剪枝
  穷举法虽然可以得到正确的解,但是它其实是对整个解空间树都进行了遍历,我们可以通过一个约束函数来对整个解空间树进行剪枝以提高算法的执行速度。

#include <iostream>
using namespace std;


int bound(int i,int n,int *v){
	int sum = 0;
	while(i<n){
		sum+=v[i++];
	}
	return sum;
}
//i表示当前处理的是第i个商品
//n表示商品总的数量 
//B背包的总重量
//cw背包当前所装物品的总重量

void dfs(int i,int n,int B,int cw,int cv,int *w,int *v,int *recode,int &max,int *result){
	if(i>=n){
		max = cv;
		for(int i=0;i<n;i++) result[i] = recode[i];
		return ;
	}
	if(cw+w[i]<=B){//当前商品可以放得下
		recode[i]=1;
		dfs(i+1,n,B,cw+w[i],cv+v[i],w,v,recode,max,result);
	}
	if(cv+bound(i+1,n,v)>max){//如果不放当前商品,剩下的商品总价值+目前已有的总价值>最大价值,则继续深搜 
		recode[i]=0;
		dfs(i+1,n,B,cw,cv,w,v,recode,max,result);
	}
}

int main(){
	freopen("背包问题.txt","r",stdin);
	int n,B;//初始化
	cin>>n>>B;
	int *w = new int[n];
	int *v = new int[n];
	for(int i=0;i<n;i++) cin>>w[i];
	for(int i=0;i<n;i++) cin>>v[i];
	
	int * recode = new int[n];
	int * result = new int[n];//存放最优方案
	int max_v = -1;
	dfs(0,n,B,0,0,w,v,recode,max_v,result);
	cout<<max_v<<endl;
	for(int i=0;i<n;i++) cout<<result[i]<<" "; cout<<endl;
	return 0;
} 

递归
  剪枝对穷举算法进行了改进,不必遍历整个解空间树,除此之外,是否还有方法呢?答案是有的,可以构造一个递归公式knapsackSR(h,i,c)=max{ knapsackSR(h,i-1,c),knapsackSR(h,i-1,c-wi)+pi }

#include <iostream>
using namespace std;

//从start到n,当背包容量为B时,所能产生的最大效益值 
int dfs(int start,int n,int B,int *w,int *v){
	if(start>=n)
		return 0;
	if(w[start]<=B)//如果能装下,则比较装与不装哪种情况下产生的效益值大 
		return max(dfs(start+1,n,B,w,v),dfs(start+1,n,B-w[start],w,v)+v[start]);
	else//如果装不下 
		return dfs(start+1,n,B,w,v);
}

int main(){
	freopen("背包问题.txt","r",stdin);
	int n,B;//初始化
	cin>>n>>B;
	int *w = new int[n];
	int *v = new int[n];
	for(int i=0;i<n;i++) cin>>w[i];
	for(int i=0;i<n;i++) cin>>v[i];
	
	int max_v = -1;
	cout<<dfs(0,n,B,w,v)<<endl;
	return 0;
} 

填表法

#include <iostream>
using namespace std;

int main(){
	freopen("背包问题.txt","r",stdin);
	int n,B;
	cin>>n>>B;
	int *w = new int[n];
	int *v = new int[n];
	for(int i=0;i<n;i++) cin>>w[i];
	for(int i=0;i<n;i++) cin>>v[i];
	
	int **map = new int*[n+1];
	for(int i=0;i<=n;i++){
		map[i] = new int[B+1];
	}
	for(int i=0;i<=n;i++)
		for(int j=0;j<=B;j++)
			map[i][j] = 0;
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=B;j++){
			if(j>=w[i-1]){
				map[i][j] = max(map[i-1][j],map[i-1][j-w[i-1]]+v[i-1]);
			}else{
				map[i][j] = map[i-1][j];
			}
		}
	}
	cout<<map[n][B]<<endl;
	return 0;
} 

滚动数组

#include <iostream>
using namespace std;

int main(){
	freopen("背包问题.txt","r",stdin);
	int n,B;
	cin>>n>>B;
	int *w = new int[n];
	int *v = new int[n];
	for(int i=0;i<n;i++) cin>>w[i];
	for(int i=0;i<n;i++) cin>>v[i];
	
	int **map = new int*[2];
	for(int i=0;i<2;i++){
		map[i] = new int[B+1];
	}
	for(int i=0;i<2;i++)
		for(int j=0;j<=B;j++)
			map[i][j]=0;
			
	for(int i=1;i<=n;i++){
		for(int j=0;j<=B;j++){
			if(j>=w[i-1]){
				map[i%2][j] = max(map[(i+1)%2][j],map[(i+1)%2][j-w[i-1]]+v[i-1]);
			}else{
				map[i%2][j] = map[(i+1)%2][j];
			}
		}
	}
	cout<<map[n%2][B]<<endl;
	return 0;
} 
  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只学弱狗!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值