动态规划入门(五)
来源:互联网 发布:淘宝店铺地址在哪里看 编辑:程序博客网 时间:2024/06/10 01:28
今天,我们初步接触背包问题,主要针对于背包中的完全背包和01背包的原理及拓展进行讲述。
1.01背包
01背包指在一个有n件物品,一个容积为m的背包这样一个情况下,使能装下的物品价值最高。(w[i]表示重量,c[i]表示价值)
拿到这一题,首先想是否满足于DP的原理(因为求最优值),那么一看,先列出一个阶段n,表示前n件物品,状态j表示装的物品总容积为j,那么可设:f[i][j]——表示前i件物品构成j的体积的最大价值,那么如何写状态转移方程呢?因为是01背包,所以可以取或不取,那么就分成了两种情况:
①如果j>=w[i],那么f[i][j]=max(f[i-1][j],f[i-1][j-a[i]]+c[i]);//这是可以取的情况。
②如果j小于w[i],那么f[i][j]=f[i-1][j];//这是取不了的情况。
结果就出来了,那么我们可以对应写出程序:
#include<stdio.h>#include<stdlib.h>#include<iostream>using namespace std;int w[1001],c[1001];int f[1001][1001];int main(){ int i,j,k,n,m; cin>>m>>n; for(i=1;i<=n;i++) cin>>w[i]>>c[i]; for(i=1;i<=n;i++) for(j=m;j>0;j--) if(j>=w[i]) f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]); else f[i][j]=f[i-1][j]; printf("%d\n",f[n][m]); return 0;}
拓展:01背包的优化:
我们要想到,因为这个算法是O(n*m)的,空间容量也是如此,那么一旦m比较大,那会爆空间啊!怎么办呢?这使我们要用到一个神奇的数组——滚动数组。
我们如果用f[j]表示不超过j公斤的最大价值,其他变量不变,那么状态转移方程怎么写呢?
f[j]=max(f[j],f[j-a[i]]+c[i]);
这样就能够优化空间了,具体看程序:
#include<stdio.h>#include<stdlib.h>#include<iostream>using namespace std;int w[20001],c[20001],f[20001];int main(){ int i,j,k,n,m; cin>>m>>n; for(i=1;i<=n;i++) cin>>w[i]>>c[i]; for(i=1;i<=n;i++) for(j=m;j>=w[i];j--) f[j]=max(f[j],f[j-w[i]]+c[i]); cout<<f[m]<<endl; return 0;}
2.完全背包
完全背包不同于01背包的,在于完全背包有n种物品,每种有无限个,那么我们还是按照01背包的思路想,设f[i][j]表示前i种取出重量不超过j的最大价值,那么状态转移方程就出来了(因为每一件物品虽然可以取无数件,但是因为不能超过上限,所以最多取m/w[i]件):
f[i][j]=max(f[i-1][j],f[i-1][j-k*w[i]]+k*c[i]);
程序如下:
#include<stdio.h>#include<stdlib.h>#include<iostream>using namespace std;int w[2001],c[2001],f[2001][2001];int main(){ int i,j,k,n,m; cin>>n>>m; for(i=1;i<=n;i++) cin>>w[i]>>c[i]; for(i=1;i<=n;i++) for(j=m;j>=1;j--) for(k=0;k<=m/w[i];k++) if(k*w[i]<=j)f[i][j]=max(f[i][j],f[i-1][j-w[i]*k]+c[i]*k); cout<<f[n][m]<<endl; return 0;}
那么,有没有更方便的呢?我们能不能优化时间复杂度呢?答案是肯定的,我们想想,01背包的j从后往前推是为了不让每一个物件多取,而完全背包正是需要让每一个物件多取,所以我们完全可以把j的循环顺序倒过来,从0~v,那么看程序:
#include<stdio.h>#include<stdlib.h>#include<iostream>using namespace std;int w[2001],c[2001],f[1001][1001];int main(){ int i,j,k,n,m; cin>>m>>n; for(i=1;i<=n;i++) cin>>w[i]>>c[i]; for(i=1;i<=n;i++) for(j=1;j<=m;j++) if(j>=w[i])f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]); else f[i][j]=f[i-1][j]; cout<<f[n][m]<<endl; return 0;}
这时,我们就转换到了一个01背包,那么空间还能不能优化呢?再一次掌声请出滚动数组,程序如下:
#include<stdio.h>#include<stdlib.h>#include<iostream>using namespace std;int w[2001],c[2001],f[2001];int main(){ int i,j,k,n,m; cin>>m>>n; for(i=1;i<=n;i++) cin>>w[i]>>c[i]; for(i=1;i<=n;i++) for(j=1;j<=m;j++) if(j>=w[i])f[j]=max(f[j],f[j-w[i]]+c[i]); cout<<f[m]<<endl; return 0;}
拓展:完全背包用二进制转化成01背包
我们在上文讲过,因为完全背包中的每一件物品最多取m/w[i]个,那么我们可以把他们按照二进制分解成为01背包,他们可以按照二进制的系数分组,如:1,2,4……,如果不够再分,那么就剩下的就自成一组!具体见代码:
#include<stdio.h>#include<stdlib.h>#include<iostream>using namespace std;int w[100001],c[100001],f[100001];int main(){ int i,j,k,n,m,a,b; cin>>m>>n; int n1=0; for(i=1;i<=n;i++){ cin>>a>>b; k=m/b; int t=1; while(k>=t){ w[++n1]=a*t;c[n1]=b*t; k-=t;t*=2; } c[++n1]=b*k;w[n1]=a*k; } for(i=1;i<=n1;i++) for(j=m;j>=w[i];j--) f[j]=max(f[j],f[j-w[i]]+c[i]); cout<<f[m]<<endl; return 0;}
推荐几篇动态规划的博客
六,五,四,三,二,一
- 动态规划入门(五)
- 动态规划入门(二)
- 动态规划入门(三)
- 动态规划入门(四)
- 动态规划入门(六)
- 动态规划入门(转)
- 简单动态规划集(五)
- 动态规划示例五
- 五、动态规划
- 防卫导弹(动态规划入门题)
- 关于动态规划(入门篇)
- 关于动态规划(入门篇)
- Tyvj1044数字三角形(动态规划入门)
- 转载:关于动态规划(入门篇)
- 动态规划(dp)入门解题报告
- [动态规划]POJ3616(dp入门题)
- 动态规划入门之LCS(2)
- 51nod(动态规划入门)
- 你不知道的颠覆式创新者
- 博弈模板
- 稀缺性与渴望——使用技巧
- 基于jdk和cglib实现的动态代理
- 未知性与好奇心——使用技巧
- 动态规划入门(五)
- Android 6 camera.open()异常解决方法
- 520. Detect Capital
- 损失与逃避心——使用技巧
- 博弈原理
- 剑指offer-面试题43-n个骰子的点数
- React Native学习(1):怎么快速学习一门新技术
- 程序员,如何从平庸走向理想?
- jqgrid多字段排序参数