背包类型DP

1、背包问题--竞赛真理

每个物品有多种采用方式

http://www.rqnoj.cn/problem/160

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
//每样物品有两种选择方法
//value[1] pri[1] value[2] pri[2]
int value[3][40],pri[3][40];
int f[1080002]; 
int n,t;
int main(){
	scanf("%d %d",&n,&t);
	for(int i=1;i<=n;i++){
		scanf("%d %d %d %d",&value[1][i],&pri[1][i],&value[2][i],&pri[2][i]);
	}
	for(int i=1;i<=n;i++){
		for(int j=t;j>0;j--){
			for(int k=1;k<=2;k++){
				if(j>pri[k][i]){
					f[j]=max(f[j],f[j-pri[k][i]]+value[k][i]);
				}
			}
		}
	}
	printf("%d
",f[t]);
return 0;
}

 2、分组背包:金明的预算方案

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
//有约束的背包问题
//在考虑主件的时候一起考虑附件
//当有1个附件的时候,有两种方案
//有2个附件的时候,有4中方案
int num[100];//有多少个东西
int f[100][100][3]; //第二维的时候数字(表示策略的种数),第三维 1 表示重要度*价值, 2 表示代价
int value[100],imp[100],q[100]; 
int n,m;
int dp[32001];
int type[100] ; //这个标识选取方法数 
void pre(){ //预处理策略 
	for(int i=1;i<=m;i++){
		if(num[i]==1){
			type[i]=1; //只有主件 
		}
		else if(num[i]==2){
			//有一个附件
			type[i]=2; 
		}
		if(num[i]>=2){
			//先处理2+主件 
			 f[i][2][1]+=f[i][1][1];
			 f[i][2][2]+=f[i][1][2];
		}
		if(num[i]==3){
			type[i]=4; //有两个附件
			f[i][3][1]+=f[i][1][1];f[i][3][2]+=f[i][1][2]; //3+主件
			f[i][4][1]=f[i][2][1]+f[i][3][1]-f[i][1][1]; //两个附件都带上
			f[i][4][2]=f[i][2][2]+f[i][3][2]-f[i][1][2];
		}
	}
}
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d %d %d",&value[i],&imp[i],&q[i]);
		if(!q[i]){
			num[i]++;
			f[i][1][1]=value[i]*imp[i];
			f[i][1][2]=value[i];
		}
		else{
			num[q[i]]++;
			f[q[i]][num[q[i]]][1]=value[i]*imp[i];
			f[q[i]][num[q[i]]][2]=value[i];
		}
	}
	pre();
	for(int i=1;i<=m;i++){
		if(num[i]>=0){
			for(int j=n;j>=0;j--){
				for(int k=1;k<=type[i];k++){  //策略数 
					if(f[i][k][2]<=j){
						dp[j]=max(dp[j],dp[j-f[i][k][2]]+f[i][k][1]);
					}
				}
			}
		}
	}
	printf("%d
",dp[n]);
return 0;
}

  

复习

  • 01背包:每个东西只有一份
cin>>m>>n;
	for(int i=1;i<=n;i++) cin>>w[i]>>c[i];
	for(int i=1;i<=n;i++){
		for(int j=m;j>=w[i];j--){   //逆序 
			f[j]=max(f[j-w[i]]+c[i],f[j]); //这里是J 
		}
	}
	cout<<f[m]<<endl;
  • 完全背包:每个东西可以取无限份
cin>>m>>n;
	for(int i=1;i<=n;i++) cin>>w[i]>>c[i];
	for(int i=1;i<=n;i++){//顺序 
		for(int j=w[i];j<=m;j++) if(f[j]<f[j-w[i]]+c[i]) f[j]=f[j-w[i]]+c[i]; 
	}
	cout<<"max="<<f[m]<<endl;
  • 混合背包:有的物品有多个有的物品只有1个,分别进行处理,逆序or正序,注意选择多个的时候循环次序,物品数量在外层,背包重量在内层
cin>>m>>n;
	for(int i=1;i<=n;i++) cin>>w[i]>>c[i]>>s[i];
	for(int i=1;i<=n;i++){
		if(s[i]==0){ //完全背包,顺序 
			for(int j=w[i];j<=m;j++){
				f[j]=max(f[j],f[j-w[i]]+c[i]);
			}
		}
		else {//01背包和多重背包,逆序 
			for(int j=1;j<=s[i];j++)  //注意顺序,先是物品个数,再是剩余重量 
			for(int k=m;k>=w[i];k--)
			f[k]=max(f[k],f[k-w[i]]+c[i]); //这里没有j噢 
		}
	}
	cout<<f[m]<<endl;
  • 多重背包,可以通过二进制处理降低复杂度,原理是每个数都可以表示位2进制相加的形式,所以这样就把它处理为了01背包问题 
  • 	int x,y,n1=0,s,t;
    	for(int i=1;i<=n;i++){
    		cin>>x>>y>>s;
    		t=1;
    		while(s>=t){
    			w[++n1]=t*x;  //重量 
    			c[n1]=t*y;     //价值 
    			s-=t;
    			t*=2;
    		}
    		w[++n1]=s*x;
    		c[n1]=s*y;
    	} 
    	//接下来就是01背包问题 
    	for(int i=1;i<=n1;i++){  //注意数量 
    		for(int j=m;j>=w[i];j--){
    			f[j]=max(f[j],f[j-w[i]]+c[i]);
    		}
    	}
    	cout<<f[m]<<endl;
  • 二维背包问题:eg.潜水员
cin>>m>>n>>k;
	memset(f,127,sizeof(f));//赋值为一个很大的数,因为是要求最小的 
	f[0][0] =0;             //f[0][0]要初始化 
	for(int i=1;i<=k;i++) cin>>mm[i]>>nn[i]>>w[i];
	for(int i=1;i<=k;i++){
		for(int j=m;j>=0;j--){//都是01背包 费用1 
			for(int k=n;k>=0;k--){       //费用2 
				int t1=j+mm[i];
				int t2=k+nn[i];  //注意写法 ,不能在外面直接判断 
				if(t1>m) t1=m;
				if(t2>n) t2=n;
				if(f[t1][t2]>f[j][k]+w[i]) f[t1][t2]=f[j][k]+w[i]; 
			}
		}
	}
	cout<<f[m][n]<<endl;
  • 分组背包,背包分组,注意循环的顺序,最外层是组数,中间层是剩余的体积,最里层是这个组里面的物品序号
int p=0;
	cin>>v>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>c[i]>>p;
		g[p][++g[p][0]]=i;     //这种写法,合并了两个功能 
	}
	for(int i=1;i<=k;i++){   //分的组数 
		for(int j=v;j>=0;j--){  //第二层是体积 
			for(int k=1;k<=g[i][0];k++){ //这个组的物品序号 
				if(j>=w[g[i][k]]){   //注意是>=,不然结果不正确 
					int temp=g[i][k];
					if(f[j]<f[j-w[temp]]+c[temp]) f[j]=f[j-w[temp]]+c[temp];
				}
			}
		}
	}
	cout<<f[v]<<endl;
原文地址:https://www.cnblogs.com/shirlybaby/p/12294965.html