关于背包那些事

几类背包问题

1.0/1背包
顾名思义,既是指一个背包,指定一些物品质量(体积)与价值(每种物品只有一个),求出可装的最大价值。每种物品有0(不选)、1(选)两种状态。
而基本思路为dp:
已知条件有第n个物品的重量w[i],价值c[i],以及背包的总容量v
简单分析
假设当前已经处理好了前i-1个物品的所有状态,那么对于第i个物品,当其不放入背包时,背包的剩余容量不变,背包中物品的总价值也不变,故这种情况的最大价值为dp[i-1][j];当其放入背包时,背包的剩余容量会减小,背包中物品的总价值会增大 ,故这种情况的最大价值为dp[i-1][j-w[i]]+c[i]

由此可以得出状态转移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i])

代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,w[100005],c[100005],v,dp[5005][5005];
int main(){
	scanf("%d %d",&v,&n);
	for(int i=1;i<=n;i++)scanf("%d %d",&w[i],&c[i]);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=v;j++){
			if(j>=w[i])dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i]);
			else dp[i][j]=dp[i-1][j];
		}
	}
	printf("%d",dp[n][v]);
	return 0;
}

But,这个代码当遇见n或v较大时便会RE,强行改变dp数组大小有会MLE,使用map的话又很有可能TLE
于是乎,就有了降维思想!!!
通过观察以上代码,不难发现第一维枚举i时只用到了ii-1,即为更新前与更新后,那么这一维就可以省略(只需覆盖即可),再通俗地说就是只需要重量的第二维。

新的代码:

#include<iostream>
using namespace std;
const int M_AX=1e5+7;
int n,m,w[M_AX],v[M_AX],f[M_AX];
int main(){
	cin>>m>>n;
	for(int i=1;i<=n;i++)cin>>w[i]>>v[i];
	for(int i=1;i<=n;i++){
		for(int l=m;l>=w[i];l--){
			if(f[l-w[i]]+v[i]>f[l])f[l]=f[l-w[i]]+v[i];			
		}
	}	
	cout<<f[m];
	return 0;
}

完全背包
与0/1背包问题相反,完全背包在0/1背包选与不选基础上多了一个条件——每个物品都有无数个,于是就有了两种思路……
思路一:
第三层循环枚举第i个物品的选择数量。
代码在0/1背包上进行一些简单改动,在此不进行展示。
f i , j = max ⁡ k + ∞ { f i − 1 , j − k ⋅ w i + k ⋅ c i } f_{i,j}=\max\limits^{+\infin}_k\{f_{i-1,j-k\cdot w_i}+k\cdot c_i\} fi,j=kmax+{fi1,jkwi+kci}
思路二:
对方法一进行优化,省去第三层循环,状态转移方程其实与0/1背包相同,
只不过第二层循环顺序发生了改变。
对于0/1背包:for(int j=m;j>=w[i];j–)
而对于完全背包:for(int j=w[i];j<=m;j++)
与0/1背包相同,我们可以将第一维去掉来优化空间复杂度。如果理解了0/1背包的优化方式,就不难明白压缩后的循环是正向的
做一个简单的代码展示

#include<cstdio>
using namespace std;
int n,m,dp[1005],w[1005],c[1005];
int main(){
	scanf("%d %d",&m,&n);
	for(int i=1;i<=n;i++)scanf("%d %d",&w[i],&c[i]);
	for(int i=1;i<=n;i++){
		for(int j=w[i];j<=m;j++){
			if(dp[j-w[i]]+c[i]>dp[j])dp[j]=dp[j-w[i]]+c[i];			
		}
	}
	printf("%d",dp[m]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值