算法解析之感想---动态规划算法的初始化和转移

    最近一直在做动态规划算法的专题训练,做了二十道题颇有感想,写出来和大家分享一下,欢迎广大ACMer,OIer,IOIer来交流~~~

    对于动态规划各种算法书中提到的最多的一半就是分析阶段、找状态、写状态转移方程。的确这些能够证明对于一个问题,你的解题思路是否正确,但是如果要想能够完全解决一道动态规划问题,除了这些之外我感觉就是对于数据的初始化了,这就有些像”高考时父母说的不要输在起跑线上“,方向正确但是在起跑线上不能正确定位,那么最终也不会赢得一个好的结果。为此,我拿01背包为例,根据各种具体情况分析一下初始化的方法:

    第一种问法,要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞,

    这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。

    解释:

    可以这样理解:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。

    第二种问法,没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0..V]全部设为0。

    解释:

    如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。

    注释

    但是通过做题我发现有的时候,有些题题目中没有说需要完全装满,但是给的数据却需要使用装满情况的初始化数据,例如hdu3496。

状态转移的过程

    状态转移的方程和使用的数据有关系,如果使用滚动数据的话相对来说好一些,不仅会降低空间复杂度,而且还能保留上一个背包的处理结果,方便整合最终的结果。但是如果使用普通数组(一般是在滚动数据中加一个维度),那就需要在每一个状态的转移的时候考虑是否需要和上一个状态或者上一个分组的对应数值进行比较,并转移结果。

例如HDU3496中,如果使用滚动数组则状态转移方程为dp[j][k] = max(dp[j][k],dp[j-1][k-t[i]]+v[i]);

测试数组自身就可以记录上一个背包或者分组的结果进行状态结果的传递,不用人工处理。
    如果使用二维数组,则状态转移方程为:f3496[i][j][k]=max3496(f3496[i-1][j][k],f3496[i-1][j-1][k-ti]+vi);本数组不能记录上一个分组的影响,所以需要人工添加转移的过程f3496[i][j][k]=max3496(f3496[i][j][k],f3496[i-1][j][k]);从而保证f3496[i][j][k](前i个物品取j个在资源限制k下去的最值)是能够涵盖前i-1、i-2、i-3...1的最优质,否者只是当前的最优值(有些类似贪心)。

    注释

    当感觉状态转移方程没有出错的情况下WA了,一般会是初始化出错或者转移过程中是否需要考虑上一个状态或者分组的影响造成。例如(HDU3033、HDU3496)

参考文献

http://qianmacao.blog.163.com/blog/static/2033971802012225104914143/


你可能感兴趣的:(C++,动态规划)