动态规划算法学习十例之一

动态规划在查找有很多重叠子问题的情况的最优解时有效。它将问题重新组合成子问题。为了避免多次解决这些子问题,它们的结果都逐渐被计算并被保存,从简单的问题直到整个问题都被解决。因此,动态规划保存递归时的结果,因而不会在解决同样的问题时花费时间。

动态规划只能应用于有最优子结构的问题。最优子结构的意思是局部最优解能决定全局最优解。简单地说,问题能够分解成子问题来解决。

1)动态规划是运筹学中用于求解决策过程中的最优化数学方法。 当然,我们在这里关注的是作为一种算法设计技术,作为一种使用多阶段决策过程最优的通用方法。它是应用数学中用于解决某类最优化问题的重要工具。


2)如果问题是由交叠的子问题所构成,我们就可以用动态规划技术来解决它,一般来说,这样的子问题出现在对给定问题求解的递推关系中,这个递推关系包含了相同问题的更小子问题的解。动态规划法建议,与其对交叠子问题一次又一次的求解,不如把每个较小子问题只求解一次并把结果记录在表中(动态规划也是空间换时间的),这样就可以从表中得到原始问题的解。

首先让我们看一个例子:
例1:如下图有一个数字三角阵,请编一个程序计算从顶点至底的某处的一条路径,使该路径所经过的数字的和最大。每一步可沿左斜线向下或右斜线向下走。
7
5 3
4 2 1
2 1 3 7

这个问题的实质实际上是一个有向图中最长路经问题,可以用经典的图论算法或者搜索来解决。

考虑如何用搜索法来解这道题,从第一个点开始扩展,每次扩展2个可行节点,直到达到数字三角形的底部,从所有的可行路径中找出最优值,这样做的复杂度是o(2^n),当n很大的时候,普通的搜索法将不可行。

观察发现,搜索的效率低下很大程度上是因为做了大量的重复运算,因为对于任何一个节点,从他开始向下拓展的最优值是确定的,这启发了我们应当充分利用之前的运算结果。

下面我们来进行深入的分析,假如已经走第I行第J列,此时最大累加和S[I,J], 应选S[I-1,J-1],S[I-1,J+1]中较大者再加上(I,J)处的值A[I,J],即下式

S[I,J]=A[I,J]+MAX(S[I-1,J-1],S[I-1,J+1])

所以我们可以从第一行开始,向下逐行推出每一处位置的最大累加和S,最后从底行的N个S中选出最大的一个即为所求,这种算法的复杂度为o(n^2),比较搜索法,已经大大的降低了,这种充分利用已经计算出结果的方法,就叫做动态规划。

通过上面的例子,我们对“动态规划”有了一个初步认识,它所处理的问题是一个“多阶段决策问题”。我们现在对一些概念进行具体定义:

状态(State):
它表示事物的性质,是描述“动态规划”中的“单元”的量。如例1中的每个节点(指节点处的最大值)都为单个的状态。

阶段(Stage):
阶段是一些性质相近,可以同时处理的状态集合。通常,一个问题可以由处理的先后次序划分为几个阶段。如例1中的问题,每一行若干节点组成一个阶段。一个阶段既可以包含多个状态,也可以只包含一个状态。描述各阶段状态的变量称为状态变量,例1中可用S[4 , j]来表示第四阶段(即第四行)走到第j列的最大值,即第四阶段状态变量。

状态转移方程:
是前一个阶段的状态转移到后一个的状态的演变规律,是关于两个相邻阶段状态的方程,是“动态规划”的中心。

如例1中: S[I,J]=A[I,J]+MAX(S[I-1,J],S[I-1,J+1])

决策(Decision):
每个阶段做出的某种选择性的行动。它是我们程序所需要完成的选择。如例1中
MAX(S[I-1,J],S[I-1,J+1])

动态规划所处理的问题是一个“多阶段决策问题”,一般由初始状态开始,通过对中间阶段决策的选择,达到结束状态。这些决策形成了一个决策序列,同时确定了完成整个过程的一条活动路线(通常是求最优的活动路线)

动态规划的应用范围,要确定一个问题是否能用动态规划,需要考虑两个方面:


一:最优子结构
即一个问题的最优解只取决于其子问题的最优解,也就是说,非最优解对问题的求解没有影响。

二:无后效性
即一个问题被划分阶段后,阶段I中的状态只能由阶段I-1中的状态通过状态转移方程得来,与其他状态没有关系,特别是与未发生的状态没有关系,这就是无后效性。


例2:对于一个由数字组成的二叉树,求由叶子到根的一条路径的数字和的最大值.
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

从上往下计算,对于每一点,只需要保留从上面来的路径中和最大的路径的和即可。
因为在它下面的点只关心到达它的最大路径和,不关心它从那条路经上来的。

import java.util.Scanner;

public class Test{
public static void main(String args[]){
int n=5;
int a[][]={{0,0,0,0,0,7,0,0,0,0,0},
{0,0,0,0,3,0,8,0,0,0,0},
{0,0,0,8,0,1,0,0,0,0,0},
{0,0,2,0,7,0,4,0,4,0,0},
{0,4,0,5,0,2,0,6,0,5,0}};


int s[][]=new int[5][11];
s[0][5]=a[0][5];
for(int i=1;i<n;i++){
for(int j=1;j<10;j++){
s[i][j]=Math.max(s[i-1][j-1],s[i-1][j+1])+a[i][j];
}
}

System.out.println("原始数据");
for(int i=0;i<n;i++){
for(int j=0;j<11;j++){
System.out.printf("%-4d",a[i][j]);
}
System.out.println();
}

System.out.println();
System.out.println("对应的最大路径");
for(int i=0;i<n;i++){
for(int j=0;j<11;j++){
System.out.printf("%-4d",s[i][j]);
}
System.out.println();
}

System.out.println();
System.out.println("最长路径的值为:");

System.out.println(max(s[n-1]));

}

public static int max(int b[]){
int max=b[0];
for(int i=1;i<b.length;i++){
if(b[i]>max) max=b[i];
}
return max;
}

}

[img]http://dl.iteye.com/upload/attachment/0074/5126/69a81021-97b8-3cfa-8812-9b4c3d842aeb.jpg[/img]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值