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,可以得到下列递推式:

2011070521060758.jpg


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

2011070521092017.jpg

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

实现:

 
  
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,记忆递归--动态规划的另一种实现方式


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

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

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


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


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


2011070521275858.jpg

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


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


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


总结:

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

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










转载于:https://www.cnblogs.com/jmzz/archive/2011/07/05/2098630.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值