几种常见背包模版

 

·0/1背包
//二维数组版 

int n,W; cin>>n>>W; //n是物品数,W最大承受重量
//f[i][j]前i个物品,最大承重为j时的最大价值
vector<vector<int>>f(n+10,vector<int>(W+10));//不要求背包恰好装满 
vector<vector<int>>f(n+10,vector<int>(W+10,-1e8)); f[0][0]=0;//要求背包恰好装满 (即总重恰好是 j) 
vector<int>w(n+10),v=w;//重量数组,价值数组 
for(int i=1;i<=n;i++) 
cin>>w[i]>>v[i];

for(int i=1;i<=n;i++){
	for(int j=0;j<=W;j++){
		f[i][j]=f[i-1][j];//第i件物品不放
		if(j>=w[i]) //第i件物品放得下 
		f[i][j]=max(f[i][j],f[i-1][j-w[i]]+v[i]);//取最大值 
	}
}

int ans=f[n][W];

//滚动数组版
int W,n; cin>>W>>n; 
vector<int>f(W+10);//不要求恰好装满
vector<int>f(W+10,-1e8); f[0]=0;//要求恰好装满 
vector<int>w(n+10),v=w;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];

for(int i=1;i<=n;i++)
	for(int j=W;j>=w[i];j--)
	f[j]=max(f[j],f[j-w[i]]+v[i]);

int ans=f[W];

//0/1背包求方案数
int W,n; cin>>W>>n; 
vector<int>f(W+10,1);//不要求恰好装满
vector<int>f(W+10); f[0]=1;//要求恰好装满
vector<int>w(n+10); 
for(int i=1;i<=n;i++) cin>>w[i];

for(int i=1;i<=n;i++)
for(int j=W;j>=w[i];j--)
f[j]+=f[j-w[i]];

int ans=f[W];




·完全背包
//暴力:
int n,W; cin>>n>>W; //n是物品数,W最大承受重量
//f[i][j]前i个物品,最大承重为j时的最大价值
vector<vector<int>>f(n+10,vector<int>(W+10));//不要求背包恰好装满 
vector<vector<int>>f(n+10,vector<int>(W+10,-1e8)); f[0][0]=0;//要求背包恰好装满 (即总重恰好是 j) 
vector<int>w(n+10),v=w;//重量数组,价值数组 
for(int i=1;i<=n;i++) 
cin>>w[i]>>v[i];

for(int i=1;i<=n;i++){
	f[i][j]=f[i-1][j];//第i个物品不放的最大值
	for(int j=0;j<=W;j++)
	for(int k=1;k*w[i]<=j;k++)
	f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*v[i]);
}
int ans=f[n][W];

考虑优化:
f[i][j]=max{f[i-1][j],f[i-1][j-w[i]]+v[i],f[i-1][j-2*w[i]]+2*v[i],f[i-1][j-3*w[i]]+3*v[i],...};
f[i][j-w[i]]=max{f[i-1][j-w[i]],f[i-1][j-2*w[i]]+v[i],f[i-1][j-3*w[i]]+2*v[i],f[i-1][j-4*w[i]]+3*v[i],...};
==>
f[i][j]=max(f[i-1][j],f[i][j-w[i]]+v[i]);

//优化 二维数组
int n,W; cin>>n>>W; //n是物品数,W最大承受重量
//f[i][j]前i个物品,最大承重为j时的最大价值
vector<vector<int>>f(n+10,vector<int>(W+10));//不要求背包恰好装满 
vector<vector<int>>f(n+10,vector<int>(W+10,-1e8)); f[0][0]=0;//要求背包恰好装满 (即总重恰好是 j)  
vector<int>w(n+10),v=w;//重量数组,价值数组 
for(int i=1;i<=n;i++) 
cin>>w[i]>>v[i];

for(int i=1;i<=n;i++){
	for(int j=0;j<=W;j++){
		f[i][j]=f[i-1][j];
		if(j>=w[i])
		f[i][j]=max(f[i][j],f[i][j-w[i]]+v[i]);
	}
}

int ans=f[n][W];

//滚动数组
int W,n; cin>>W>>n; 
vector<int>f(W+10);//不要求恰好装满
vector<int>f(W+10,-1e8); f[0]=0;//要求恰好装满 
vector<int>w(n+10),v=w;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];

for(int i=1;i<=n;i++){
	for(int j=w[i];j<=W;j++)
	f[j]=max(f[j],f[j-w[i]]+v[i]);
}

//完全背包求方案数
int W,n; cin>>W>>n; 
vector<int>f(W+10,1);//不要求恰好装满
vector<int>f(W+10); f[0]=1;//要求恰好装满
vector<int>w(n+10); 
for(int i=1;i<=n;i++) cin>>w[i];

for(int i=1;i<=n;i++)
for(int j=w[i];j<=W;j++)
f[j]+=f[j-w[i]];

int ans=f[W];


·多重背包
//暴力
int W,n; cin>>W>>n;
vector<vector<int>>f(n+10,vector<int>(W+10));
vector<int>w(n+10),v=w,c=w;//重量、价值、个数
for(int i=1;i<=n;i++) cin>>w[i]>>v[i]>>c[i];

for(int i=1;i<=n;i++){
	for(int j=0;j<=W;j++){
		f[i][j]=f[i-1][j];
		for(int k=1;k<=c[i] and k*w[i]<=j; k++)
		f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*v[i]);
	}
}

int ans=f[n][W];


//空间优化-滚动数组
int W,n; cin>>W>>n; 
vector<int>f(W+10);
vector<int>w(n+10),v=w,c=w;
for(int i=1;i<=n;i++) cin>>w[i]>>v[i]>>c[i];

for(int i=1;i<=n;i++){
	for(int j=W;j>=w[i];j--){
		for(int k=0;k<=c[i] and k*w[i]<=j;k++)
		f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);
	}
}

int ans=f[W];

//时间优化-二进制优化
int W,n; cin>>W>>n; 
vector<int>f(W+10);
vector<int>w(n+10),v=w,c=w;
for(int i=1;i<=n;i++) cin>>w[i]>>v[i]>>c[i];

for(int i=1;i<=n;i++){
	int num=min(c[i],W/w[i]);//最多选多少个,上界优化 
	for(int k=1;num>0;k<<=1){
		if(k>num) k=num;
		num-=k;
		for(int j=V;j>=k*c[i];j--)//构造一个重量为k*w[i],价值为k*v[i]的物品,再走0/1背包 
			f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);
	}
}
int ans=f[W];

//二维费用背包
//三维
int W,M,n; cin>>n>>W>>M; 
vector<vector<vector<int>>>f(n+10,vector<vector<int>>(W+10,vector<int>(M+10)));
vector<int>w(n+10),m=w,v=w;
for(int i=1;i<=n;i++) cin>>w[i]>>m[i]>>v[i];

for(int i=1;i<=n;i++) {
	for(int j=0;j<=W;j++)
	for(int k=0;k<=M;k++){
		f[i][j][k]=f[i-1][j][k];
		if(j>=w[i] and k>=m[i])
		f[i][j][k]=max(f[i][j][k],f[i-1][j-w[i]][k-m[i]]+v[i]);
	}
}

int ans=f[n][W][M];


//空间优化

int W,M,n; cin>>n>>W>>M; 
vector<vector<int>>f(W+10,vector<int>(M+10));
vector<int>w(n+10),m=w,v=w;
for(int i=1;i<=n;i++) cin>>w[i]>>m[i]>>v[i];

for(int i=1;i<=n;i++)
for(int j=W;j>=w[i];j--)
for(int k=M;k>=m[i];k--)
f[j][k]=max(f[j][k],f[j-w[i]][k-m[i]]+v[i]);

int ans=f[W][M];


·混合背包
int W,n; cin>>W>>n; 
vector<int>f(W+10);
vector<int>w(n+10),v=w,p=w;//p是类型数组 
for(int i=1;i<=n;i++) cin>>w[i]>>v[i]>>p[i];

for(int i=1;i<=n;i++) {
	if(p[i]==-1){//0/1背包 
		for(int j=W;j>=w[i];j--) 
		f[j]=max(f[j],f[j-w[i]]+v[i]);
	}else if(p[i]==0){//完全背包 
		for(int j=w[i];j<=W;j++)
		f[j]=max(f[j],f[j-w[i]]+v[i]);
	}else{//多重背包 
		int num=min(p[i],W/w[i]);
		for(int k=1;num>0;k++){
			if(k>num) k=num; 
			num-=k; 
			for(int j=W;j>=k*w[i];j--)
			f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);
		}
	}
}

int ans=f[W];

·分组背包
int W,n; cin>>n>>W;//n是组数 
vector<int>f(W+10);
for(int i=1;i<=n;i++){
	int s;cin>>s; //第i组的物品数
	vector<int>w(s+10),v=w;
	for(int j=1;j<=s;j++) cin>>w[j]>>v[j];
	for(int j=W;j>=0;j--)//容量的循环必须在物品枚举循环的前面,顺序不能颠倒 
	for(int k=1;k<=s;k++)
		if(j>=w[k])
		f[j]=max(f[j],f[j-w[k]]+v[k]);//每个容量下的解可以反复覆盖以求得最优解 	
}
int ans=f[W];





 

24/7/29

例题:

·设状态:f[j][k]表示选j个人的分数,是否可以凑出k分(f[j][k]表示可以凑出)。

·状态转移方程:f[j][k]|=f[j-1][k-x];(考虑j-1个人的分数之外的另一个人的分数为x情况下,可以凑出k分,这j-1个人的分数值k-x).

·枚举x:    

for(int i=1;i<=n;i++)//枚举a[i](上文所述x)

 枚举j(所选人数):

	for(int j=i;j>=1;j--)//每考虑进来一个新的数,就自顶向下进行一遍状态转移,因为每个人的分数只能选一次,类似0/1背包(倒着转) 

 枚举分数:

for(int k=a[i];k<=s/2;k++)

状态转移:

f[j][k]|=f[j-1][k-a[i]];

最后倒着遍历分数找答案即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e2+10;
int f[N][N*N];//f[i][k]只选i个人的请况能否凑出k分 
int a[N];
int main() 
{
    int n,s=0;cin>>n;
    for(int i=1;i<=n;i++)
    {
    	cin>>a[i];
    	s+=a[i];
	}
	f[0][0]=1;
	for(int i=1;i<=n;i++)
	for(int j=i;j>=1;j--)//每考虑进来一个新的数,就自顶向下进行一遍状态转移,因为每个人的分数只能选一次,类似0/1背包(倒着转) 
	for(int k=a[i];k<=s/2;k++)//这里正反序都无所谓 
	f[j][k]|=f[j-1][k-a[i]];//状态转移方程 
	for(int k=s/2;k>=0;k--)
	if(f[n/2][k]){
			 cout<<k;
			 return 0;
	}
	return 0;
}

24/8/5

25/5/1

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值