【大家明白才是真的明白】如何根据动态规划数组求得最佳策略

我们知道,我们可以使用二维数组求得一个DP问题的最佳值,但是并没有求得其最佳方案。

以背包问题为例,如何根据已经求得的二维数组 dp[N+1][W+1] ,求得最佳选择方案呢?

只需要看DP代码,如下:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int dpf[N+1][W+1]; //数组从0开始  
  2. int dp_solve()  
  3. {  
  4.     for(int i=0; i<N;i++)  
  5.         for(int j=0; j<=W;j++)  
  6.             if(j<w[i])  
  7.                 dpf[i+1][j]=dpf[i][j];  
  8.             else  
  9.                 dpf[i+1][j]=max(dpf[i][j],dpf[i][j-w[i]]+v[i]);  
  10.     return dpf[N][W];  
  11. }  

我们可以看到dpf[i+1][j] 进和  dpf[i][j],dpf[i][j-w[i]]+v[i] 两个状态相关。

【规则1】找出添加新步骤的关键条件。

而且,如果和dpf[i][j]相关,那么 最优价值不会变化,所以不会添加新步骤,所以我们只需要考虑  dpf[i][j] ==dpf[i-1][j-w[i-1]]+v[i-1] 的情况。(dpf  从N开始,而w,v数组从 N-1开始往前)

【小结】以上主要找出 使得最优价值发生改变的“那个步骤”,因为只有变化了,说明这个步骤才可能是最优策略里的一步。

【规则2】DP是正向进行的,求最优方案就该从逆向考虑。

写出基本循环:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. forint i=N;i>0;i++)  
  2.     forint j=W;j>0;j++)  
  3.       

首先dpf[N][W] 必然是最优值。

【规则3】使用一个临时变量记录当前最佳值。


这里初始化为 int  bestV= dpf[N][W] ;

我们遍历时,如果发现 当前 dpf[i][j] 不等于 当前最佳值时,不用考虑,肯定不是我们需要的策略里的一步。

所以我们每次循环只需要考虑 dpf[i][j] == bestV 的情况。 

【小结】以上思想为,当前最佳值是多少,我们就找从这个最佳值出发向前找策略,就比如背包中,如果当前最佳值是 7,那么当前状态下 如果遇到 最佳值是6的dpf[q][p]可以直接忽略。

所以下面我们可以写出,寻找到 “最优策略里某个步骤” 的代码:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int bestV=dpf[N][W];  
  2. forint i=N;i>0;i++)  
  3.     forint j=W;j>0;j++)  
  4.         if ( dpf[i][j] == dpf[i-1][j-w[i-1]]+v[i-1]  && bestV==dpf[i][j])  // 这里不建议写成 bestV == dpf[i-1][j-w[i-1]]+v[i-1],因为跳转  
  5.         {  
  6.             // 这里对该步骤进行存储,便于显示  
  7.         }  
找到了当前最优步骤后,我们需要更新一些变量。

【规则4】更新当前最佳值,记录步骤等。

我们要把bestV  修改为:去掉当前步骤后的前面所有步骤组成的方案能获得的价值。

这里是  bestV =  dpf[i-1][j-w[i-1]] ;

然后我们就可以写出所有代码了,如下:


当然,针对0-1背包问题,我们可以优化这个寻找策略的方法:

比如更新最佳值时,我们也可以更新 j在每一轮i开始时的初始值,原因自己想象 【提示,DP的无后效性】

还有,对于一行元素来说,如果这行对应的步骤已经被找到,那么这行其他列就不要再考虑了,直接 break即可。 

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int bestV=dpf[N][W];  
  2. string a = "";  
  3. forint i=N;i>0;i++)  
  4.     forint j=W;j>0;j++)  
  5.         if ( dpf[i][j] == dpf[i-1][j-w[i-1]]+v[i-1]  && bestV==dpf[i][j])  
  6.         {  
  7.              a = a + char(i+48);   
  8.              bestV = dpf[i-1][j-w[i-1]];  
  9.         }  
  10. //示例输出:   1345 代表 物品 1 3 4 5被选中。  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值