任何动态规划核心就是划分子问题,然后就是状态转移方程。中间求出来的子问题的解需要存储起来。
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( 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元一共有多少种方法。
递推公式以及相应的一维如何推导为二维如下: