背包问题——动态规划

0——1背包

0-1背包:建立一个二维dp[num+1][size+1],dp[i][j]指前i件当容量为j时的最佳状况即能装的最多,状态转移方程:
j<weight[i],dp[i][j]=dp[i-1][j]
j>=weight[i]&&j<=size,dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]+value[i]),
解释:不能装下当前物件时,最好的状态就是上一个的状态,而当能装下时,又要考虑要不要装,如果不装,同上,如果装,就是在前一个状态的基础上进行计算

#include<iostream>
#include<cstring>
using namespace std;
int main(){
	int num,w; cin>>num>>w;
	int weight[num+1],value[num+1];
	for(int i=1;i<=num;i++) cin>>weight[i];
	for(int i=1;i<=num;i++) cin>>value[i];	
	
	int dp[num+1][w+1];
	memset(dp,0,sizeof(dp));

	for(int i=1;i<=num;i++)
		for(int j=0;j<=w;j++)
			if(j>=weight[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
			else dp[i][j]=dp[i-1][j];

	cout<<dp[num][w];
}

如果从后往前转移方程转化为一维数组,自行思考
0-1背包具体演示

完全背包

完全背包是想了两种
*1、*转换成0-1背包来看,先看看状态转移方程:
j<weight[i]时,dp[i][j]=dp[i-1][j];
j>=weight[i]&&j<=size时,dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
这里和之前的0-1背包所不同的是,之前的0-1背包在比较时是与i-1阶段的状态做比较,而完全背包是与当前阶段的状态做比较。因为在思考的时候是在当前阶段是否能装下更多的物品,并且与上一层的状态做比较,先放出二维dp的代码
在这里插入图片描述

#include<iostream>
#include<cstring>
using namespace std;
int main(){
	int num,size;cin>>num>>size;
	int weight[num+1],value[num+1];
	for(int i=1;i<=num;i++) cin>>weight[i];
	for(int i=1;i<=num;i++) cin>>value[i];
	
	int dp[num+1][size+1];
	memset(dp,0,sizeof(dp));
	
	for(int i=1;i<=num;i++){
		for(int j=0;j<weight[i]&&j<=size;j++) dp[i][j]=dp[i-1][j];
		for(int j=weight[i];j<=size;j++) dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
	}
	
	
	cout<<dp[num][size];
}

然后这是简化成一维dp数组的代码

#include<iostream>
#include<cstring>
using namespace std;
int main(){
	int num,size;cin>>num>>size;
	int weight[num+1],value[num+1];
	for(int i=1;i<=num;i++) cin>>weight[i];
	for(int i=1;i<=num;i++) cin>>value[i];
	
	int dp[size+1];
	memset(dp,0,sizeof(dp));
	
	for(int i=1;i<=num;i++)
	for(int j=weight[i];j<=size;j++) dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
	
		for(int j=0;j<=size;j++)
		cout<<dp[j]<<"   ";
		cout<<endl;
	
	cout<<dp[size];
}

2、
这个是分别考虑kweight[i]时的最优状态
状态转移方程为:dp[i][j]=max(dp[i][j],dp[i][j-k
weight[i]+value[i])
因为k有多重循环,所以在比较的时候应该与自己作比较
先给出二维代码

#include<iostream>
#include<cstring>
using namespace std;
int main(){
	int num,size;cin>>num>>size;
	int weight[num+1],value[num+1];
	for(int i=1;i<=num;i++) cin>>weight[i];
	for(int i=1;i<=num;i++) cin>>value[i];
	
	int dp[num+1][size+1];
	memset(dp,0,sizeof(dp));
	
	for(int i=1;i<=num;i++){
		for(int j=0;j<=size;j++){
			for(int k=0;k*weight[i]<=j;k++){
				dp[i][j]=max(dp[i][j],dp[i-1][j-k*weight[i]]+k*value[i]);
			}
		}
	}
	cout<<dp[num][size]<<endl;
} 

这是优化的二维数组代码

#include<iostream>
#include<cstring>
using namespace std;
int main(){
	int num,size;cin>>num>>size;
	int weight[num+1],value[num+1];
	for(int i=1;i<=num;i++) cin>>weight[i];
	for(int i=1;i<=num;i++) cin>>value[i];
	
	int dp[size+1];
	memset(dp,0,sizeof(dp));
	
	for(int i=1;i<=num;i++){
		for(int j=0;j<=size;j++){
			for(int k=0;k*weight[i]<=j;k++){
				dp[j]=max(dp[j],dp[j-k*weight[i]]+k*value[i]);
			}
		}
	}
	cout<<dp[size]<<endl;
} 

3、多重背包
在这里插入图片描述
多重背包就是在完全背包的基础上对k多了重限制而已,原本是for(int k=0;kweigh[i]<=j;k++)变成了for(int k=0;kweight[i]<=j&&k<=num[i];k++)
并且,在num[i]*weight[i]>=j时,这就等同于完全背包了,所以放出代码

#include<iostream>
#include<cstring>
using namespace std;
int main(){
	int n,size;cin>>n>>size;
	int weight[n+1],value[n+1],num[n+1];
	for(int i=1;i<=n;i++) cin>>weight[i];
	for(int i=1;i<=n;i++) cin>>value[i];
	for(int i=1;i<=n;i++) cin>>num[i];
	
	int dp[n+1][size+1];
	memset(dp,0,sizeof(dp));
	
	for(int i=1;i<=n;i++)
		if(num[i]*weight[i]>=size){
			for(int j=0;j<weight[i]&&j<=size;j++) dp[i][j]=dp[i-1][j];
			for(int j=weight[i];j<=size;j++) dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
		}else
		for(int j=0;j<=size;j++)
			for(int k=0;k*weight[i]<=j&&k<=num[i];k++){
				dp[i][j]=max(dp[i][j],dp[i-1][j-k*weight[i]]+k*value[i]);
	}
	
	cout<<dp[n][size];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值