0-1背包问题(0-1背包,动态规划,C++)

题目来源:acwin

方法一:(二维数组)

状态转移方程dp[ i ] [ j ]:

(设第i件物品的体积和价值分别为w[ i ]和val[ i ])
如果第i件物品的体积大于容量j,则物品i不能装入背包,此时dp[ i ] [ j ]=dp[ i-1 ] [ j ]。

如果第i件物品的体积不大于容量j,则分两种情况,一是把第i件物品装入背包,此时dp[ i ] [ j ]=dp[ i-1 ] [ j-w[i] ]+val[ i ];二是不把第i件物品装入背包,此时dp[i][j]=dp[i-1][j]。取二者中大的为最终dp[i][j],即dp[i] [j]=max( dp[ i-1 ][ j-w[i] ]+val[ i ], dp[ i-1 ][ j ] )

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

int w[1010],v[1010];
int dp[1010][1010];

int main(){
	int N,Vol;
	cin>>N>>Vol;
	for(int i=1;i<=N;i++){
		cin>>v[i]>>w[i];
	}
	for(int i=1;i<=N;i++){
		for(int j=1;j<=Vol;j++){
			if(j<v[i]){
				dp[i][j]=dp[i-1][j];
			}
			else{
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
			}
		}
	}
	cout<<dp[N][Vol]<<endl;
	return 0;
}

另外,v[i]和w[i]可以边输入边处理,这样就可以节省处理数据的时间。

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

int dp[1010][1010];

int main(){
	int N,Vol;
	int v,w;
	cin>>N>>Vol;
	for(int i=1;i<=N;i++){
		cin>>v>>w;
		for(int j=1;j<=Vol;j++){
			if(j<v){
				dp[i][j]=dp[i-1][j];
			}
			else{
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w);
			}
		}
	}
	
	cout<<dp[N][Vol]<<endl;
	return 0;
}

方法二:

由于dp[ i ]只与dp[ i-1 ]有关,所以如果想要节省空间,定义dp[2] [1010]就行了。即来回覆盖。

int solve(int N,int Vol){
	int _new=0,old=1;
	for(int i=1;i<=N;i++){
		swap(_new,old);
		for(int j=0;j<=Vol;j++){
			if(v[i]>j)dp[_new][j]=dp[old][j];
			else{
				dp[_new][j]=max(dp[old][j],dp[old][j-v[i]]+w[i]);
			}
		}
	}
	return dp[_new][Vol];
}

方法三:(一维数组)

按二维数组来讲,我们只需要输出dp[N][Vol]就行了,但我们却求出了从1到N的所有情况。因此我们用一个一维数组来回覆盖也能求出最终的结果。对于N来说,只涉及到dp[i]=dp[i-1],因此我们从小到大进行覆盖不会影响最终的结果。但对于体积Vol来说,我们可能会用第 i-1 轮的较小体积 j-v[i]来更新成第 i 轮的较大体积 j,如果体积Vol也是从小到大,那么我们将先更新体积小的,也就是说我们将用第 i 轮的较小体积 j-v[i] 更新得到第 i 轮的较大体积 j,这显然是错误的,因为我们应该用第 i-1 轮的较小体积 j-v[i] 来更新第 i 轮的较大体积 j。但如果对于体积,我们从大到小进行循环则可以避免这个错误,因为当从大到小进行循环时,较小体积 j-v[i] 还未被更新,也就是说用的是第 i-1 层的较小体积 j-v[i] ,而不是第 i 层的 j-v[i]。

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

int dp[1010];

int main(){
	int N,Vol;
	int v,w;
	cin>>N>>Vol;
	for(int i=1;i<=N;i++){
		cin>>v>>w;
		for(int j=Vol;j>=1;j--){
			if(j<v){
				dp[j]=dp[j];
			}
			else{
				dp[j]=max(dp[j],dp[j-v]+w);
			}
		}
	}
	cout<<dp[Vol]<<endl;
	return 0;
}

另外,上述代码中的if语句 dp[j]=dp[j] 可省略,且只有在 j>=v 时才会执行语句 dp[j]=max(dp[j],dp[j-v]+w); 因此代码可以进一步简化为

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

int dp[1010];

int main(){
	int N,Vol;
	int v,w;
	cin>>N>>Vol;
	for(int i=1;i<=N;i++){
		cin>>v>>w;
		for(int j=Vol;j>=v;j--){
			dp[j]=max(dp[j],dp[j-v]+w);
		}
	}
	cout<<dp[Vol]<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值