基础背包---01;完全;01背包之2;多重(二进制优化);二维费用流;分组
1.01背包:
选或不选这件
记dp[i+1][j]为从0到i这i+1个物品中挑选 总重小于j时,总价值的最大值
dp[i+1][j]=dp[i][j](j<w[i]时)
dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i])(其他)
int dp[MAX_N+1][MAX_M+1]; int v[i],w[i]; int W//总重量不超过W void solve(){ for(int i=0;i<n;i++){ for(int j=0;j<=W;j++){ if(j<w[i])dp[i+1][j]=dp[i][j]; else dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]); } } printf("%d\n",dp[n][W]); }
2.完全背包:
一件东西有无限件,可以取无限次
记dp[i+1][j]为从0到i这i+1个物品中挑选 总重小于j时,总价值的最大值
如果和01背包类似的话,就有三重循环,复杂度过高
但是发现在dp[i+1][j]的计算中选择k(k>=1)个的情况,和在dp[i+1][j-w[i]]的计算中选择k-1个的情况时一样的
所以:
dp[i+1][j]=dp[i][j](j<w[i]时)
dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i])(其他)
int dp[MAX_N+1][MAX_M+1]; int v[i],w[i]; int W//总重量不超过W void solve(){ for(int i=0;i<n;i++){ for(int j=0;j<=W;j++){ if(j<w[i])dp[i+1][j]=dp[i][j]; else dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i]); } } printf("%d\n",dp[n][W]); }
01: int n,i,j,m,w[35],v[35],dp[205];// 最多30个物品,最大容量m=200 cin>>m>>n; for(i=1;i<=n;i++)cin>>w[i]>>v[i];//重量,价值 for(i=1;i<=n;i++) for(j=m;j>=w[i];j--)//逆序 dp[j]=max(dp[j-w[i]]+v[i],dp[j]); cout<<dp[m]<<endl; 完全: int n,m,i,j,w[32],c[32],dp[202]; scanf("%d%d",&m,&n);//容量,种数 for(i=1;i<=n;i++)cin>>w[i]>>v[i];//重量,价值 for(i=1;i<=n;i++) for(j=w[i];j<=m;j++) dp[j]=max(dp[j],dp[j-w[i]]+v[i]); printf("max=%d",dp[m]);
3.01背包之2
背景和01背包一样,都是计算不超过W重量的最大价值
但是条件变了:
1<=n<=100
1<=wi<=10^7
1<=vi<=100
1<=W<=10^9
之前01背包的复杂度是O(nW),所以之前的方法肯定不行
现在W太大,反而vi小了,所以从vi入手
记dp[i+1][j]为从0到i这i+1个物品中挑选 价值为 j 时,总重量的最小值(不存在就是INF)
所以dp[0][0]=0,dp[0][j]=INF
dp[i+1][j]=dp[i][j](j<v[i]时)
dp[i+1][j]=max(dp[i][j],dp[i][j-v[i]]+w[i])(其他)
int dp[MAX_N+1][MAX_N*MAX_V+1]; int v[i],w[i]; int W;//最大重量 void solve(){ for(int i=0;i<n;i++){ for(int j=0;j<=MAX_N*MAX_V;j++){ if(j<v[i])dp[i+1][j]=dp[i][j]; else dp[i+1][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]); } } inr res=0; for(int i=0;i<=MAX_N*MAX_V;i++)if(dp[n][i]<=W)res=i; printf("%d\n",res); }
技巧:如果要求恰好装满背包,那么初始化dp[0][0]=0;dp[0][j]=-INF(因为要求恰好装满:只有容量为j=0时在啥也不装的情况下有0这个解,容量为j(j>0)时则无解)
若没有要求必须装满,只是希望价格尽量大,初始化就应全设为0;(因为不要求装满,则可以假设啥也不装,那么解就可以都为0)
4.多重背包:
多重背包问题的思路跟完全背包的思路非常类似,只是k的取值是有限制的,因为每件物品的数量是有限制的,状态转移方程为:
dp[i+1][j] = max{dp[i][j - k * w[i]] + v[i] | 0 <=k <= n[i]}
当W<=n[i]*w[i]时,可以无限取(总重不超过W)同完全背包
当W>n[i]*w[i]时,同01背包;将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为1,2,4,...,2^(k-1),n[i]-2^k+1;使得选择该物品的最佳个数都可由这些系数相加得来
例题:tzoj5736
#include<bits/stdc++.h> using namespace std; int m[2005],w[2005],s[2005];//个数 重量 价值 int n,W,dp[5005]; void zeroone(int w,int v) { int i; for(i=W; i>=w; i--) dp[i]=max(dp[i],dp[i-w]+v); } void com(int w,int v)//完全 { for(int i=w;i<=W;i++)dp[i]=max(dp[i],dp[i-w]+v); } void multidp(int w,int v,int cnt) { if(cnt*w>=W) { com(w,v); return; } int k=1; while(k<=cnt)//二进制优化 { zeroone(k*w,k*v); cnt-=k; k*=2; } zeroone(cnt*w,cnt*v); return ; } int main() { int i,j,k; scanf("%d%d",&n,&W);//物品数,最大体重 for(i=0;i<n;i++)scanf("%d%d%d",&m[i],&w[i],&s[i]); for(i=0;i<n;i++)multidp(w[i],s[i],m[i]); printf("%d\n",dp[W]); } 5736
5.二维费用流背包:
在一维基础上再加一维,其他和上面的背包一样
例题:(toj 5742)(01背包加一维)
#include<bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f int dp[1005][25][85]; int n,m,k,a[1005],b[1005],w[1005]; int main() { int i,j,p; scanf("%d%d",&m,&n); scanf("%d",&k); for(i=0;i<k;i++)scanf("%d%d%d",&a[i],&b[i],&w[i]); for(i=0;i<=m;i++){ for(j=0;j<=n;j++)dp[0][i][j]=INF; } dp[0][0][0]=0; for(i=0;i<k;i++){ for(j=0;j<=m;j++){ for(p=0;p<=n;p++){ int x=j-a[i],y=p-b[i]; int rec;//记录选这件 要付出的重量 if(x<0&&y>=0)rec=dp[i][0][y]+w[i]; else if(x>=0&&y<0)rec=dp[i][x][0]+w[i]; else if(x<0&&y<0)rec=dp[i][0][0]+w[i]; else if(x>=0&&y>=0)rec=dp[i][x][y]+w[i]; dp[i+1][j][p]=min(dp[i][j][p],rec); } } } printf("%d\n",dp[k][m][n]); }
6.分组背包:
最外层循环每组;中间和01背包一样循环W,最内层循环每个组的物品--保证每组只选一个或不选
例题:tzoj:5743
#include<bits/stdc++.h> using namespace std; int n,V,t,w[31],c[31],dp[202]; vector<int>v[12]; int main() { int i,j,k;scanf("%d%d%d",&V,&n,&t); for(i=0;i<n;i++){ int p; scanf("%d%d%d",&w[i],&c[i],&p); v[p].push_back(i); } for(i=1;i<=t;i++){ for(j=V;j>=0;j--){ for(k=0;k<v[i].size();k++){ int u=v[i][k]; if(j>=w[u])dp[j]=max(dp[j],dp[j-w[u]]+c[u]);//注意if判断 } } } printf("%d\n",dp[V]); } /* 10 1 3 2 1 1 */