您的位置:首页 > 其它

动态规划-背包问题降维以及衍生的硬币问题

2016-06-08 11:20 274 查看
任何动态规划核心就是划分子问题,然后就是状态转移方程。中间求出来的子问题的解需要存储起来。

0-1 背包问题:

0-1 背包问题解决的是物品只能够选择1次要不就不选,在背包能够装得下的前提下面,能够保证装的价值最大:
状态转移方程 f[i][j] = max{ f[i-1][j] , f[i-1][j - wi] +vi }; 0≤j - wi≤j
用二维的空间来表示是比较简单的
一维表示:f[v] 表示 f[i][v] , 那么由于上面的这个公式: f[i][j] = max{ f[i-1][j] , f[i-1][j - wi] +vi }; 
可以发现f[i][j] 的值只是和f[j-1][j] , 和f[i-1][j-wi] ,如果我们写出如下的代码:

for(int i = 0 ; i < n ; i++){
    for(int j = m ; j >=0  ; j--){
        vec[j] = max(vec[j] , vec[j-w[i]]+v[i] );
    }
}
那么对于i = N 的情况,我们计算 vec [j] 时, 由于j-wi < j ,那么vec[j-w[i]]的值会在vec[j]后参与计算,所以,在计算vec[j ] 的时候,我们先读取到的vec[j] 实际上是vec[N-1][j] , 读取到的 vec[j-w[i]]实际上是vec[N-1][j-w[i]],所以可以用上面的公式进行简化。

完全背包问题

完全背包问题中每个物品都可以选择无限多次



从上面的公式中可以看出来,f(i , j)只会和f(i-1, j )还有f(i,j-wi)两个变量有关,外层的i是从0开始累加的,我们考虑i=n的这次循环情况。此时,j将会从0开始累加,那么,我们计算f( j )的步骤如下 :  
首先读取到的f( j ) , 一定是f( n-1 , j ) (因为我们的外层计算中的 i 是从0开始加的)
然后我们会读取f(j-wi),因为这个j-wi<j, 所以f(i-wi)的值已经经过了计算,表示的是f(n,j-wi)
到这里,我们只需要将f( j )和f(j-wi)取max,求出来的值用来更新f(j),此时的f(j)就是f(n,j)了。至此,二维空间将为一维。
生成代码就是
for(int i = 0 ; i < n ; i++){
    for(int j = 0 ; j < m ; j++){
        vec[j] = max(vec[j] , vec[j-w[i]]+v[i] );
    }
}

硬币问题:

硬币问题和完全背包问题类似,原题是这样的:有1分,2分,5分,10分四种硬币,每种硬币数量无限,给定n分钱,有多少中组合可以组成n分钱?
采用动态规划的思路,f(i,j)表示的是可以取的0,1,...,i种硬币的情况下,凑够j元一共有多少种方法。
递推公式以及相应的一维如何推导为二维如下:



降维原因和完全背包类似。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: