动态规划3 背包总结及其优化
来源:互联网 发布:方正字体排查软件 编辑:程序博客网 时间:2024/06/02 06:18
一.01背包的一维空间优化
1.只定义一个一维数组来记录当前重量下的最大值情况不断更新(虽然优化了空间复杂度但是这样只能求出最大值而不能求出最优解)
2.核心算法:best[j]=best[j]>best[j-w[i]]+v[i]?best[j]:best[j-w[i]]+v[i];(放入还是不放入)
3.关于第二重循环(重量)为何逆序判断:我们来看二位递推公式:backpack[i][j]=backpack[i-1][j]>(backpack[i-1][j-wi[i]]+value[i])?backpack[i-1][j]:backpack[i-1][j-wi[i]]+value[i];
可以看出:f[i][j]的状态是由f[i-1][j]推出来的也就是说我们现在要求的状态是上一个已经存在的状态推出来的
<1>如果逆循环:从dp[C]开始,找dp[C-w[i]]来求dp[C],而dp[C-w[i]]是上一次已经存在的状态,所以符合二维递推,由已存在的上一状态来推下一状态。
<2>如果顺序循环:从dp[0]开始,把以前存在的状态都更新了,以后求的dp[C]是在更新了的状态下推出来的,所以肯定不对!
综上:第二重循环选择逆序!!
4.代码:
#include <stdio.h>#include<string.h>int w[1000],v[1000];int best[1000];int main(){ int n,C; while(scanf("%d%d",&n,&C)!=EOF) { int i,j,t; memset(best,0,sizeof(best)); for(i=1; i<=n; i++) scanf("%d",&w[i]); for(i=1; i<=n; i++) scanf("%d",&v[i]); for(i=1; i<=n; i++) { for(j=C; j>=w[i]; j--)//逆序!j>=w[i]使j-w[i]>=0而有意义 { best[j]=best[j]>best[j-w[i]]+v[i]?best[j]:best[j-w[i]]+v[i]; } } printf("%d\n",best[C]); } return 0;}二.完全背包解析及其优化
1.题意:在01背包基础上增加了有无限多个物品
2.解法一:三重循环枚举物品个数情况
代码:
#include <stdio.h>#include<string.h>int w[1000],v[1000];int best[1000][1000];int main(){ int n,C; while(scanf("%d%d",&n,&C)!=EOF) { int i,j,t; memset(best,0,sizeof(best)); for(i=1; i<=n; i++) scanf("%d",&w[i]); for(i=1; i<=n; i++) scanf("%d",&v[i]); for(i=1; i<=n; i++) { for(j=0; j<=C; j++) { int k=j/w[i]; for(t=0; t<=k; t++)//枚举物品个数 { best[i][j]=best[i][j]>best[i-1][j-t*w[i]]+t*v[i]?best[i][j]:best[i-1][j-t*w[i]]+t*v[i];//** } } } printf("%d\n",best[n][C]); } return 0;}
注:**处一个方程原因:1.当j<w[i]也就是该物品放不进去的时候:k=0,best[i][j]=best[i][j]>best[i-1][j]?best[i][j]:best[i-1][j];符合放不进去的时候情况,而且就判断这一次和不判一样
2.当j>=w[i]的时候,也就是可以放,这时候选择放还是不放,不放k=0:同一,符合best[i][j]=best[i-1][j];放k>0,判断最大也符合,并不断更新
3.综上一个方程即可,不用再加判断条件。
解法二:优化时间复杂度减少循环
1.注:由各种画图实验计算得:在best[i][j]的选择k种(k>=1)情况计算中,与在best[i][j-w[i]]的计算中选择k-1个情况是相同的,所以best[i][j]的递推中k>=1部分情况早已经在best[i][j-w[i]]中计算完了,best[i][j-w[i]]就是best[i][j]当k>=1时候的最大值,所以用其比较当k=0也就是不放的时候大小取最大即可。
2.代码:
#include <stdio.h>#include<string.h>int w[1000],v[1000];int best[1000][1000];int main(){ int n,C; while(scanf("%d%d",&n,&C)!=EOF) { int i,j,t; memset(best,0,sizeof(best)); for(i=1; i<=n; i++) scanf("%d",&w[i]); for(i=1; i<=n; i++) scanf("%d",&v[i]); for(i=1; i<=n; i++) { for(j=0; j<=C; j++) { if(j<w[i]) best[i][j]=best[i-1][j];//不能放 else best[i][j]=best[i-1][j]>best[i][j-w[i]]+v[i]?best[i-1][j]:best[i][j-w[i]]+v[i];//放还是不放 } } printf("%d\n",best[n][C]); } return 0;}
解法三:空间优化
1.v变化的区间是顺序循环的原因:完全背包的特点是每种物品可选无限件,在求解加选第i种物品带来的收益f[i][v]时,在状态f[i][v-c[i]]中已经尽可能多的放入物品i了,此时在f[i][v-c[i]]的基础上,我们可以再次放入一件物品i,此时也是在不超过背包容量的基础下,尽可能多的放入物品i。
2.代码:#include <stdio.h>#include<string.h>int w[1000],v[1000];int best[1000];int main(){ int n,C; while(scanf("%d%d",&n,&C)!=EOF) { int i,j,t; memset(best,0,sizeof(best)); for(i=1; i<=n; i++) scanf("%d",&w[i]); for(i=1; i<=n; i++) scanf("%d",&v[i]); for(i=1; i<=n; i++) { for(j=w[i]; j<=C; j++)//注意区间!! { best[j]=best[j]>best[j-w[i]]+v[i]?best[j]:best[j-w[i]]+v[i]; } } printf("%d\n",best[C]); } return 0;}
以上:背包问题。