白话完全解析动态规划原理及相关问题(二)
来源:互联网 发布: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列表,这样比较节约空间,因为我们有用的,只是当前得到的最大值,之前的那些结果一步步被覆盖掉就可以了。
后续,我们将进一步讲解动态规划的内容,及其其适用条件。
- 白话完全解析动态规划原理及相关问题(二)
- 白话完全解析动态规划原理及相关问题(一)
- 动态规划之背包问题(二):完全背包问题
- 动态规划原理解析
- 动态规划原理解析
- 动态规划之二见完全背包问题
- 完全背包问题 动态规划
- 动态规划-完全背包问题
- 完全背包问题-动态规划
- 动态规划之完全背包问题
- 完全背包问题动态规划c++
- scala 动态规划解决完全背包问题
- 动态规划初步-完全背包问题
- 动态规划问题详解(二)
- 动态规划算法分析及实例——求解完全背包问题(java实现)
- java 动态规划策略原理及例题
- java 动态规划策略原理及例题
- 动态规划算法介绍与相关问题。
- 安卓生成、显示二维码APP代码实现
- 揭开AssetBundle庐山真面目(二)
- nginx开机自动启动脚本
- 禅宗与道教
- 通过 GCC 学习 OpenMP 框架
- 白话完全解析动态规划原理及相关问题(二)
- [USACO2.2]循环数 Runaround Numbers
- Python 启动/停止脚本(后台服务)
- js面向对象组件开发---拖拽
- snort规则检测引擎初探
- HDU 1166 敌兵布阵(树状数组)
- 【SSH进阶之路】Hibernate基本原理(一)
- Orz教主第6次模拟赛之教主的别墅
- bootstrap开发小网页实例