zoukankan      html  css  js  c++  java
  • 背包类型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;
  • 相关阅读:
    希腊字母写法
    The ASP.NET MVC request processing line
    lambda aggregation
    UVA 10763 Foreign Exchange
    UVA 10624 Super Number
    UVA 10041 Vito's Family
    UVA 10340 All in All
    UVA 10026 Shoemaker's Problem
    HDU 3683 Gomoku
    UVA 11210 Chinese Mahjong
  • 原文地址:https://www.cnblogs.com/shirlybaby/p/12294965.html
Copyright © 2011-2022 走看看