背包问题(入门基础版)

目录

一、【NOIP2001】装箱问题

1.)二维基础版:

2.)01滚动

3.)“就地滚动” ​编辑

01背包

 完全背包

 多重背包

二维费用背包问题

 分组背包


一、【NOIP2001】装箱问题

传送门-(牛客)

 

 

 【动画演示真的yyds好嘛^^】

重中之重  状态转移方程 f[i][j] = f[i-1][j] || f[i-1][ j-v[i] ] 其实就是逆着想;

对第 i 个物品就两种选择:要 或 不要;

在 f[i-1][ j-v[i] ] 状态时 有,可以在此基础上放入目前的i 号物品,得到  f[i][j] 有;->此为“要”的选择;另一种情况,在上一次,即  f[i-1][j] 时已经有(意为有 j 体积了),这一轮第 i 个物品不要,所以体积还是原来的 j , 即 f[i][j] ;

其中有 “继承” 上一轮状态 并新添本次选择了的物品后的状态  的感觉。

理解了后  用最易懂的二维数组版写:

1.)二维基础版:

#include<iostream>
using namespace std;
int v,n,a[40],f[40][20010];
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>v>>n;
	f[0][0]=1;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		for(int j=0;j<=v;j++){
			if(j>=a[i])f[i][j]=(f[i-1][j]||f[i-1][j-a[i]]);
			else f[i][j]=f[i-1][j];
		}
	}
	for(int i=v;i>=0;i--){
		if(f[n][i]){
			cout<<v-i;
			return 0;
		}
	}
}

2.)01滚动

更新第0行后,更新第1行,再要更新第2行的时候,发现只与它的前一行也就是第1行有关,第0行已经没用了,这时将第2行装到第0行里,后面再要写第3行时,写到第1行里……依此类推,也就是i , i-1变成了i%2, (i-1)%2等等

//01滚动 
#include<iostream>
using namespace std;
int v,n,a[40],f[2][20010];
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>v>>n;
	f[0][0]=1;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		for(int j=0;j<=v;j++){
			if(j>=a[i])f[i%2][j]=(f[(i-1)%2][j]||f[(i-1)%2][j-a[i]]);
			else f[i%2][j]=f[(i-1)%2][j];
		}
	}
	for(int i=v;i>=0;i--){
		if(f[n%2][i]){    // 注意最后判断的数组是n%2不一定是1 
			cout<<v-i;
			return 0;
		}
	}
}

3.)“就地滚动” 

 

//就地滚动  倒序更新 
#include<iostream>
using namespace std;
int v,n,a[40],f[20010];
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>v>>n;
	f[0]=1;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		for(int j=v;j>=a[i];j--){
			f[j]=f[j]||f[j-a[i]];  //不要落写"f[j]||" 
		}
	}
	for(int i=v;i>=0;i--){
		if(f[i]){
			cout<<v-i;
			return 0;
		}
	}
}

01背包

 完全背包

 多重背包

 

 

二维费用背包问题

 

 

 分组背包

 

 【特别注意 “所有的 i 属于组k ”的循环要在V的循环里面,因为要保证这一组的每个物品都是只选它时的情况,如果k循环放到了外面,那就会变成同一组中的物品a在基于同组物品b已选的情况下转移得到,而事实上它们会起冲突……】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值