2018.02.06
背包专卖店系列
今天我们学习了背包问题,浏览了一个规模宏大的背包专卖店。。。领略了许许多多的背包。
0 - 1 背 包;完 全 背 包;多 重 背 包;混 合 背 包;部 分 背 包;
二 维 费 用 背 包;分 组 背 包;有 依 赖 背 包;
1. 0-1背包
思路:设$f[i][v]$表示前$i$件物品,总重量不超过$v$的最优价值,则$f[i][v]=max(f[i-1][v-w[i]]+c[i],f[i-1][v])$;$f[n][m]$即是最优解。
核心代码:
$O(n^2)$空间复杂度版
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int w[101],c[101]; 5 int f[101][1001]; 6 int _Max(int x,int y){return x>y?x:y;} 7 int main(){ 8 int n,m; 9 int i,j,v; 10 scanf("%d%d",&m,&n); 11 for(i=1;i<=n;i++) 12 scanf("%d%d",&w[i],&c[i]); 13 for(i=1;i<=n;i++) 14 for(v=m;v>0;v--) 15 if(w[i]<=v) f[i][v]=_Max(f[i-1][v],f[i-1][v-w[i]]+c[i]); 16 else f[i][v]=f[i-1][v]; 17 printf("%d\n",f[n][m]); 18 return 0; 19 }
$O(n)$空间复杂度版
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int m,n; 5 int w[31],c[31]; 6 int f[2001]; 7 int main(){ 8 int i,j,v; 9 scanf("%d%d",&m,&n); 10 for(i=1;i<=n;i++) 11 scanf("%d%d",&w[i],&c[i]); 12 for(i=1;i<=n;i++){ 13 for(v=m;v>=w[i];v--) 14 if(f[v-w[i]]+c[i]>f[v]) 15 f[v]=f[v-w[i]]+c[i]; 16 } 17 printf("%d",f[m]); 18 return 0; 19 }
状态:AC
2.完全背包
思路:设$f[i][v]$表示前$i$件物品,总重量不超过$v$的最优价值,则$f[i][v]=max(f[i][v-w[i]]+c[i],f[i-1][v])$;$f[n][m]$即为最优解。
核心代码:
$O(n^2)$空间复杂度版
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int m,n; 5 int w[31],c[31]; 6 int f[31][201]; 7 int main(){ 8 int i,j,v; 9 scanf("%d%d",&m,&n); 10 for(i=1;i<=n;i++) 11 scanf("%d%d",&w[i],&c[i]); 12 for(i=1;i<=n;i++){ 13 for(v=1;v<=m;v++){ 14 if(v<w[i]) 15 f[i][v]=f[i-1][v]; 16 else if(f[i-1][v]>f[i][v-w[i]]+c[i]) 17 f[i][v]=f[i-1][v]; 18 else f[i][v]=f[i][v-w[i]]+c[i]; 19 } 20 } 21 printf("%d",f[n][m]); 22 return 0; 23 }
$O(n)$空间复杂度版
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int m,n; 5 int w[31],c[31]; 6 int f[2001]; 7 int main(){ 8 int i,j,v; 9 scanf("%d%d",&m,&n); 10 for(i=1;i<=n;i++) 11 scanf("%d%d",&w[i],&c[i]); 12 for(i=1;i<=n;i++){ 13 for(v=w[i];v<=m;v++) 14 if(f[v-w[i]]+c[i]>f[v]) 15 f[v]=f[v-w[i]]+c[i]; 16 } 17 printf("%d",f[m]); 18 return 0; 19 }
状态:AC
3.多重背包
思路:和完全背包相类似,只是物品取的次数不是一次,也不是无限次,而是有限的$n[i]$次,所以只要把状态转移方程改成$f[i][v]=max{f[i][v-k*w[i]]+k*c[i]}(0<=k<=n[i])$;$f[n][m]$为最优解。
核心代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int v[6002],w[6002],s[6002]; 5 int f[6002]; 6 int m,n; 7 int _Max(int x,int y){return x>y?x:y;} 8 int main(){ 9 int i,j; 10 scanf("%d%d",&n,&m); 11 for(i=1;i<=n;i++) 12 scanf("%d%d%d",&v[i],&w[i],&s[i]); 13 for(i=1;i<=n;i++){ 14 for(j=m;j>=0;j--) 15 for(int k=0;k<=s[i];k++){ 16 if(j-k*v[i]<0)break; 17 f[j]=_Max(f[j],f[j-k*v[i]]+k*w[i]); 18 } 19 } 20 printf("%d",f[m]); 21 return 0; 22 }
状态:AC
4.混合背包
思路:把前三种背包问题都综合起来,(其中0-1背包和完全背包类似,所以可以合并起来)用一个$if$来判断该物品是属于哪一种问题,再处理即可。
核心代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int m,n; 5 int w[31],c[31],p[31]; 6 int f[201]; 7 int _Max(int x,int y){return x>y?x:y;} 8 int main(){ 9 int i,j,k,v; 10 scanf("%d%d",&m,&n); 11 for(i=1;i<=n;i++) 12 scanf("%d%d%d",&w[i],&c[i],&p[i]); 13 for(i=1;i<=n;i++){ 14 if(p[i]==0){ 15 for(j=w[i];j<=m;j++) 16 f[j]=_Max(f[j],f[j-w[i]]+c[i]); 17 } 18 else { 19 for(j=1;j<=p[i];j++) 20 for(k=m;k>=w[i];k--) 21 f[k]=_Max(f[k],f[k-w[i]]+c[i]); 22 } 23 } 24 printf("%d",f[m]); 25 return 0; 26 }
状态:AC
5.二维费用背包
思路:费用加了一维,状态也只需加一维,设$f[i][v][u]$表示前$i$件物品付出两种代价分别为$u$和$v$时可获得的最大价值。状态转移方程:$f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+c[i]}$
核心代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int v,u,k; 5 int a[1001],b[1001],c[1001]; 6 int f[101][101]; 7 int main(){ 8 int i,j; 9 memset(f,127,sizeof(f)); 10 f[0][0]=0; 11 scanf("%d%d%d",&v,&u,&k); 12 for(i=1;i<=k;i++) 13 scanf("%d%d%d",&a[i],&b[i],&c[i]); 14 for(i=1;i<=k;i++) 15 for(j=v;j>=0;j--) 16 for(int l=u;l>=0;l--){ 17 int t1,t2; 18 t1=j+a[i]; 19 t2=l+b[i]; 20 if(t1>v)t1=v; 21 if(t2>u)t2=u; 22 if(f[t1][t2]>f[j][l]+c[i]) 23 f[t1][t2]=f[j][l]+c[i]; 24 } 25 printf("%d",f[v][u]); 26 return 0; 27 }
6.分组背包
思路:???未理解,状态转移方程:$f[k][v]=max{f[k-1][v],f[k-1][v-w[i]]+c[i]}$;$f[k][v]$表示前$k$组物品花费$v$费用所获得的最大价值。
核心代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <stdio.h> 2 #include <math.h> 3 #include <string.h> 4 int v,n,t; 5 int w[31],c[31]; 6 int a[11][32],f[201]; 7 int main(){ 8 int i,j,k; 9 scanf("%d%d%d",&v,&n,&t); 10 for(i=1;i<=n;i++){ 11 int p; 12 scanf("%d%d%d",&w[i],&c[i],&p); 13 a[p][++a[p][0]]=i; 14 } 15 for(k=1;k<=t;k++) 16 for(j=v;j>=0;j--) 17 for(i=1;i<=a[k][0];i++) 18 if(j>=w[a[k][i]]){ 19 int tmp=a[k][i]; 20 if(f[j]<f[j-w[tmp]]+c[tmp]) 21 f[j]=f[j-w[tmp]]+c[tmp]; 22 } 23 printf("%d",f[v]); 24 return 0; 25 }
状态:AC
P.S. 袁老师课后的习题我今天没做,我回家以后一定补上