dp 01背包避免重复计算

来源:互联网 发布:mac怎么截图快捷键 编辑:程序博客网 时间:2024/05/20 19:48

前几天第一次接触到01背包,也就是最基本的背包。当时也是看了很久的,然后大体都理解了,掺杂着状态方程与各种图画描述理解的。

基本的实现代码大概是这样子的

//n代表物体数,V代表背包的体积,weight为物体的重量for(i=0;i<n;i++)<span style="white-space:pre"></span>for(j=V;j>=0;j--){if( j>=v[i] && dp[j-v[i]]+weight[i]>dp[i] )dp[i]=dp[j-v[i]]+weight[i];}

当时对于第二个循环 j=V从V开始,而不是从0开始,有点迷惑,后来理解了是为了避免重复计算:

比如说:当你更新第5个物品的时候(此时你各个体积的背包中的属性是当前物品当前体积下的最大重量),你必须且只能借用你更新了第四个物品后的背包去更新,假若j=V改成j=0,就会出现这样的情况 dp[5]已经是第五个物品下更新完后的状态了,当后面遇到dp[8]的更新要是用到dp[5]的话那么就会导致dp[8]重复计算了,所以必须要从后面更新到前面。

昨天有接触到了一个方案数背包的题目:hdu 2126

然后 “haha593572013”当时的有一部分的写法是

 for(int i=1;i<=n;i++)          {           for(int j=0;j<v[i];j++)dp[i][j][0]=dp[i-1][j][0],dp[i][j][1]=dp[i-1][j][1];              for(int j=v[i];j<=m;j++)              {                  if(dp[i-1][j-v[i]][0]+1>dp[i-1][j][0])                  {                      dp[i][j][0]=dp[i-1][j-v[i]][0]+1;                      dp[i][j][1]=dp[i-1][j-v[i]][1];                  }                  else if(dp[i-1][j-v[i]][0]+1==dp[i-1][j][0])                  {                      dp[i][j][0]=dp[i-1][j][0];                      dp[i][j][1]=dp[i-1][j][1]+dp[i-1][j-v[i]][1];                  }                  else                   {                      dp[i][j][0]=dp[i-1][j][0];                      dp[i][j][1]=dp[i-1][j][1];                  }              }          }  
j是从0开始的,后来发现(还是以上面那个例子),它更新第五个物品的时候仍然是用到更新完第四个物品的背包,

虽然此时j是从0开始,但是由于它是二维数组,所以,更新完第四个物品的状态都保存在上一层的数组,不会改变,因此就不会出现重复计算。

如:

 dp[i][j][0]=dp[i-1][j-v[i]][0]+1;
更新第i个物品时,是用到上一层更新完后的dp[i-1]下的状态,所以dp[i-1]是不会发生改变的。


最后写了一天的背包,多少有点理解:

首先是写出状态转移方程,找到思路。

然后给首层状态也就是“0”层状态(什么都没有操作下的状态)初始化,然后根据状态方程递推下去,求到第n层的解,总体有点像数学归纳。


根据状态方程写递推代码的时候,以hdu 2126为例.它的状态方程为

if(k>=p[i])  dp[i][j][k]+=dp[i-1][j-1][k-p[i]];  dp[i][j][k]+=dp[i-1][j][k];  
只需要看dp的前两维,我们需要求dp[i][j],状态的转移都是来自于dp[i-1][j-1]或者是dp[i-1][j],这两者的状态都需要在dp[i][j]之前准备好,就可以递推了,如何准备好这两者的状态呢,明显,这么做就可以满足

 <span style="font-size:24px;">for(int i=1;i<=n;i++)              for(int j=1;j<=i;j++)  </span>


这样就可以将dp[i-1]层的状态全部(全部,指的是i-1时对应的k=1,2......i)设置好,则dp[i]层便可以利用它进行转移

或者这么写

<span style="font-size:24px;"> for(int j=1;j<=n;j++)       for(int i=j;i<=n;i++) </span>

反正确认准备好了...就可以了,不用太纠结这点的..



最最后,状态递推的过程中,一定要保证状态的独立性,避免重复计算。

(此文是初学者接触背包几天后写下的复习笔记,若有错误或者可以改善的地方,或者有可以延伸到动态规划的知识深入系统的说明的地方,请各位大牛多多指点)


0 0
原创粉丝点击