白话完全解析动态规划原理及相关问题(二)

来源:互联网 发布:c语言hex bcd 编辑:程序博客网 时间:2024/06/11 09:09

上一会,我们说到了动态规划的基本原理与思路,下面,我们以一个具体的例子来感受一下动态规划的求解过程。
问题来自:
http://blog.csdn.net/baidu_28312631/article/details/47418773
这篇博文,本来我是想用一个矩阵连乘问题为例,看到这个北京大学的OJ题目不错,题目描述简单,求解过程全面,就以这个题目为例吧。
问题描述:
这里写图片描述
在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为 0 - 99

输入格式:5      //表示三角形的行数    接下来输入三角形73   88   1   02   7   4   44   5   2   6   5

在上面提到的那篇博文里面,使用CPP对算法进行描述,这里就不再使用CPP进行描述了,我们改用类似伪代码的Python进行描述,大家如果有习惯CPP描述的,可以查阅上面给出的博文。
好了,言归正传,没有动态规划知识的童鞋,请先看上一篇博文,了解一下大体的思路和方法。
我们先来看下这道题的代码:

import sysn = int((sys.stdin.readline()))val = [[0 for i in range(n)] for i in range(n)]#定义一个二维数组,有点冗余了,max_value = 0for i in range(0,n):    str = sys.stdin.readline().split(" ")    for j in range(len(str)):        val[i][j]=int(str[j]) #将标准输入流读取到数组中def step(x,y): #递归函数    print("x y",x,y) #输出一下,看看递归路径    if x == n-1:#下标要减1        return val[x][y]    else:        return (max(step(x+1,y+1),step(x+1,y))+val[x][y]) #递归公式print(step(0,0))#入口

这是一种应用递归的方式来实现的代码,递归公式是:
这里写图片描述
这是我抄来的,懒得画了。
我们简单说说这个公式是怎么设计出来的:
我们知道,递归的过程实际上是一个栈,当所有的函数都已经过入栈了,最开始执行的,自然是最后面被调用的函数,对应着就是这里面当r=N,也就是到上面的数字三角形最底下的位置,对应图中来看,这是一种从低向上计算的过程。
这里写图片描述
以图中4为例,它需要返回的结果(也就是最大值)是这个数本身,加上其下面与其相邻的两个数。这样一层层地,从下面向上返回数据,到最后7的位置,栈全部清空,也就是程序都执行完毕,结果也就出来了。
我们看下结果:

573 88 1 02 7 4 44 5 2 6 5x y 0 0x y 1 1x y 2 2x y 3 3x y 4 4x y 4 3x y 3 2x y 4 3x y 4 2x y 2 1x y 3 2x y 4 3x y 4 2x y 3 1x y 4 2x y 4 1x y 1 0x y 2 1x y 3 2x y 4 3x y 4 2x y 3 1x y 4 2x y 4 1x y 2 0x y 3 1x y 4 2x y 4 1x y 3 0x y 4 1x y 4 030

最后迭代出来的结果是30,这里能够看出来,以(x,y)为参数的step函数是有好多重复执行的,那也就是说,这个过程是不必要的,如果减少了这些重复执行的过程,那么程序的复杂度也会相应减少。
上一次,我们说过,这里面可以建立一个表来代替重复运算。
代码如下:

import sysn = int((sys.stdin.readline()))val = [[0 for i in range(n)] for i in range(n)]#定义一个二维数组,有点冗余了,res = [[-1 for i in range(n)] for i in range(n)]#结果记录表step_count = 0for i in range(0,n):    str = sys.stdin.readline().split(" ")    for j in range(len(str)):        val[i][j]=int(str[j]) #将标准输入流读取到数组中def step(x,y): #递归函数    global step_count    step_count = step_count +1    print("x y",x,y) #输出一下,看看递归路径    if res[x][y] != -1:        return res[x][y]    else:        if x == n-1:#下标要减1            return val[x][y]        else:            res[x][y] = (max(step(x+1,y+1),step(x+1,y))+val[x][y]) #递归公式            return res[x][y]print(step(0,0))print(step_count)

执行结果为:

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
x y 0 0
x y 1 1
x y 2 2
x y 3 3
x y 4 4
x y 4 3
x y 3 2
x y 4 3
x y 4 2
x y 2 1
x y 3 2
x y 3 1
x y 4 2
x y 4 1
x y 1 0
x y 2 1
x y 2 0
x y 3 1
x y 3 0
x y 4 1
x y 4 0
30
21

看到,这里只需21步即可完成,而上一次没有建立res表的时候,需要执行31步。

然而,这是动态规划吗?当然不是,我们看这段代码:

import sysn = int((sys.stdin.readline()))val = [[0 for i in range(n)] for i in range(n)]#定义一个二维数组,有点冗余了,step_count = 0for i in range(0,n):    str = sys.stdin.readline().split(" ")    for j in range(len(str)):        val[i][j]=int(str[j]) #将标准输入流读取到数组中def step2(): #递归函数    global step_count    vector = [i for i in val[n-1]]    for t in range(-(n-2),1):#转换为下标,从倒数第二行,到最上面,注意 range(1)只是执行到0        x = -t        for y in range(0, x+1):            vector[y] =( val[x][y] + max(vector[y],vector[y+1]))            step_count = step_count + 1    print(vector)step2()print(step_count)

执行结果:

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
[30, 21, 10, 10, 5]
10

看到,整个过程仅仅用了10步迭代,他的思路是,从最下面到最上面,一次找到最优的,存在vector 列表中,那么列表中第一个值,就是最优的结果了。
我们用一个图来看:
这里写图片描述

以2为例,可以和最后一行4相加,也可以和最后一行的5相加,但是很显然和5相加要更大一点,结果为7,将7保存起来,然后分析数字7,7可以和最后一行的5相加,也可以和最后一行的2相加,结果与5相加最大,保存为12,以此类推,直至求出嘴上面的结果,我们在代码中这些最大值保存的形式是覆盖了vector列表,这样比较节约空间,因为我们有用的,只是当前得到的最大值,之前的那些结果一步步被覆盖掉就可以了。

后续,我们将进一步讲解动态规划的内容,及其其适用条件。

原创粉丝点击