DP之背包问题+记忆递归


2个问题:

1)背包问题的动态规划解法
2)动态规划的另一种实现方式,记忆递归(dp的第一篇文章就提到过,professional中也提到过),在这里来讲解,加上全面所有的文章,这一点应该可以算是动态规

划里最后一个没有详细介绍的关键点了


---------------------------------------------------------------------------------------------------------------------------------------------------


给定一组物品:

重量为 w1 , w2 , .......wn

价值为 v1 , v2 , .........vn

和一个称重量为W的书包。

求这些物品的一个最有价值的子集,可以装到书包中去。


--------------------------------------------------------------------------------------------------------------------------------------



1,背包问题


设 V[ i , j ]表示能够放进称重量为 j 的背包的前 i 个物品的最有价值子集的价值,目标是求V[ n , W ]

根据 V[ i , j ] 的最佳子集中是否包含物品 i,可以得到下列递推式:




这个递推式的意思是:如果 i 的重量已经比 j 大了,那显然不含 i (  j-wi < 0 ), 如果 i 的重量比 j 小,那么可能包含 i (如果包含能去到最大值的话)。



这个表可以按行也可以按列填:

实现:

package Section8;

/*第八章 动态规划 背包问题*/

public class BackPack {

/**
*
@param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] w = { 2, 1, 3, 2 }; // 重量数组
int[] v = { 12, 10, 20, 15 }; // 价值数组
int W = 5;

int[][] result = backPack(w, v, W);

for (int i = 0; i < result.length; i++)
{
for (int j = 0; j < result[0].length; j++)
System.out.print(result[i][j]
+ " ");
System.out.println();
}
}

public static int[][] backPack(int[] w, int[] v, int W) {
// w是物品重量数组,v事物品价值数组,W是背包重量
// 返回表达背包问题求解过程的矩阵
int n = w.length; // w和v的长度是相同的
int[][] result = new int[n + 1][W + 1]; // 前i个物品(i从0到n),W从0到W

// 初始条件:result[0][j] = 0;result[i][0] = 0;
for (int i = 0; i <= W; i++)
result[
0][i] = 0;
for (int i = 0; i <= n; i++)
result[i][
0] = 0;

// 根据动态规划的状态转移方程填表:这个表格可以一行一行的填,也可以一列一列的填,这里采用一行一行的填
// 注意填表方式是动态规划里面非常重要的一个东西,当你填某一个位置时,它需要用到的其他位置必须都已经填好
// 所以填表的方式是跟状态转移方程相关滴,深层次来说,是跟动态规划构造解的生成过程相关的

for (int i = 1; i <= n; i++) // 行数从1到n
{
for (int j = 1; j <= W; j++) // 列数从1到W
{
// 此时决定了一个i,j的位置要填:result[i][j]
if (j - w[i - 1] < 0)
result[i][j]
= result[i - 1][j];
else
result[i][j]
= max(result[i - 1][j], v[i - 1] + result[i - 1][j - w[i - 1]]);
}
}

return result;
}

public static int max(int m, int n) {
if (m >= n)
return m;
return n;
}

}


结果:

0     0     0     0     0     0    
0     0     12     12     12     12    
0     10     12     22     22     22    
0     10     12     22     30     32    
0     10     15     25     30     37    


写了这么几个动态规划,已经可以发现,有了递推式后,其编程就显得非常easy,基本上就是照着公式操作矩阵。

因此,应重点关注动态规划的思想,怎么去描述和刻画问题,发现最有子结构,得到其状态转移方程。



-------------------------------------------------------------------------------------------------------------------------------------------------



2,记忆递归--动态规划的另一种实现方式


关于什么什么是记忆递归,它的出发点是什么,为什么要记忆递归,这些在前面都提过,这里再简单说下:

动态规划的核心思想之一就是记忆(或者记录)来避免对重复子问题进行求解,然而,它并没有避免对不必要子问题进行求解。

若将递归与动态规划结合起来,就可以得到一种动态规划的递归实现方式,这种方式避免了对不必要子问题进行求解。


实际上就是在递归的时候,在动态规划表中先检查要递归的项是否已经求出来了,如果已经求出来了,就直接返回答案,否则才去递归求解。


背包问题的记忆递归实现:




实际上,就是在V[ i , j]还没有求出来的时候(一旦一个V[ i , j]求出来了,就不会再变,这点很重要),才去递归,否则就直接返回值。


想想斐波拉契数列也可以记忆递归的去实现。


-------------------------------------------------------------------------------------------------------------------------------------------------


总结:

1) 背包问题的时间复杂度和空间复杂度都是 nW,2个循环,见代码

2)一个关键点:动态规划的另一种实现方式,记忆递归










posted @ 2011-07-05 20:39  jinmengzhe  阅读(3207)  评论(0编辑  收藏  举报