数字三角形(数塔问题)


其实动态规划本身并不是一个特定的算法,是一种用途广泛的问题求解方法,一种思想,一种手段。

1.1问题描述与状态定义


有一个有非负整数组成的三角形,第一行一个数字,下面各行除了最后一行外,每行的每个数字下面左右各一个数字。
如图示:

从第一行数字开始,每次只能走左下或右下一格,直到走到最后一行,把沿途的走过的所有数字加起来。
如何能使这个和最大?

【问题复杂度分析】如果熟悉回溯法,就会立即发现这是一个动态的决策问题:每次两个选择----左下或右下。
但是如果选择用回溯法解决此问题,惯常的问题就是效率太低:一格n层的数字三角形的完整路线有2^n条,所以当n很大时完全不能靠此方法。

因此为了提高效率,需要把此问题抽象:把所有的位置(i,j)抽象为一个个不同的状态,然后定义状态(i,j)的指标函数d(i,j)为从格子(i,j)出发时能得到的最大的和(包括此格子本身)。在这个定义下,原问题的解是d(1,1)。

如下图:



 
【状态转移分析】从格子(i,j)出发有两种决策。若往左走,则走到(i+1,j)后续要求“从(i+1,j)出发后能得到的最大和“这一问题,即是d(i+1,j)。类似的,往右做之后要求解d(i,j+1)。由于这两个选择自由可选,所以,应该选择较大的。即得到了所谓的状态转移方程:

d(i,j) = a(i,j) + max { d(i+1, j), d(i, j+1)  }

最优子结构:如果往左走,最好的情况是(i,j)格子里面的值a(i,j) 与”从(i+1,j)出发后能得到的最大和"之和, 注意"最大"两个字,如果从(i,j)出发到底部这部分都不是最大的话,加上a(i,j)也必然不是最大的。这就是最优子结构。或描述全局最优解包含局部最优解。


【总结】动态规划的核心是状态和状态转移方程。

1.2 解决方法:记忆化搜索与递推


方法一:递归计算

代码如下:(注意边界)
int d(int i, int j)
{
return a[i][j] +( i == n ? 0 : d(i+1, j) >? d(i, j+1) ) ;
}

如此计算正确只是效率依然不高,问题在于重复计算。就是一些格子被两个父节点所共有,所以,在递归的时候,便会被重复计算。



方法二: 递推计算


代码如下:
(注意此时重复边界的处理)
int  i, j;
for (j=1; j<=n; j++)
d[n][j] = a[n][j] ;
for (i=n-1; i>=1; i--)
for (j=1; j<=i; j++)
{
d[i][j] = a [i][j] + d[i+1][j] >? d[i][j+1] ;
}

时间复杂度显然是O(n^2),
可以如此计算的原因在于:
i是逆序枚举的,因此在计算d[i] [j] 之前,他所需要的d[i+1][j] 和 d[i][j+1] 已经计算出来了。

提示:在多数情况下,可以采用递推法计算状态转移方程。递推的关键在于边界和计算顺序。多数情况下,递推法的时间复杂度是:状态转移方程X每个状态的决策数X决策时间。


方法三:记忆化搜索

程序分为两部分。首先用数组函数memset();将数组整体置为-1,然后写递归函数:
int d(int i, int j)
{
if(d[i][j >= 0) return d[i][j] ;
return d[i][j] = a[i][j] +( i == n ? 0 : d(i+1, j) >? d(i, j+1) ) ;
}

依然是递归函数,同时把计算结果存在数组d中。题目说各个数字均为非负数,因此如已经计算过某个d[i][j],那么期应该是非负数。
这样只需要把所有的数组元素初始化为负数,如-1,就可以知道是否计算过d[i][j].

注意:不要忘记把结果存在数组d[i][j]中。根绝c语言的”赋值语句本身有返回值“的规定,可以把保存d[i][j]的工作整合在函数的返回语句中。

上述的方法三称为记忆化,虽然不像地推算法那样显示的指明了计算的顺序,但是任然可以保证每个节点只访问了一次。

由于i和j都在1~n之间,所以所有的不同的节点之间一共有O(n^2)个。即是时间复杂度。

提示:可以用记忆化搜索的方法计算状态转移方程。采用记忆化搜索时,不必事先确定个状态的计算顺序,但需要记住每个状态是否计算过。


1.3程序实战练手

HDOJ--2084


未完待续...................
下一节:DAG上的(DP)

算法入门系列一--DP初步的更多相关文章

  1. 数据结构与算法入门系列教程-C#

    数据结构与算法入门系列教程 (一)为啥要学习数据结构与算法 曾经我也以为自己很牛逼,工作中同事也觉得我还可以,领导也看得起我,啥啥啥都好,就这样过了几年,忽然发现自己学新东西没劲.时代都变了,而我还只 ...

  2. 算法入门系列2:k近邻算法

    用官方的话来说,所谓K近邻算法(k-Nearest Neighbor,KNN),即是给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例(也就是上面所说的K个邻居), 这K个 ...

  3. 算法入门系列1:k-means

    k-means是一种无监督学习算法,用于聚类. 下图(来自http://www.cnblogs.com/jerrylead/archive/2011/04/06/2006910.html)展示了k-m ...

  4. 数据挖掘入门系列教程(二)之分类问题OneR算法

    数据挖掘入门系列教程(二)之分类问题OneR算法 数据挖掘入门系列博客:https://www.cnblogs.com/xiaohuiduan/category/1661541.html 项目地址:G ...

  5. 数据挖掘入门系列教程(三)之scikit-learn框架基本使用(以K近邻算法为例)

    数据挖掘入门系列教程(三)之scikit-learn框架基本使用(以K近邻算法为例) 简介 scikit-learn 估计器 加载数据集 进行fit训练 设置参数 预处理 流水线 结尾 数据挖掘入门系 ...

  6. 数据挖掘入门系列教程(四点五)之Apriori算法

    目录 数据挖掘入门系列教程(四点五)之Apriori算法 频繁(项集)数据的评判标准 Apriori 算法流程 结尾 数据挖掘入门系列教程(四点五)之Apriori算法 Apriori(先验)算法关联 ...

  7. 数据挖掘入门系列教程(五)之Apriori算法Python实现

    数据挖掘入门系列教程(五)之Apriori算法Python实现 加载数据集 获得训练集 频繁项的生成 生成规则 获得support 获得confidence 获得Lift 进行验证 总结 参考 数据挖 ...

  8. 前端学习 node 快速入门 系列 —— 初步认识 node

    其他章节请看: 前端学习 node 快速入门 系列 初步认识 node node 是什么 node(或者称node.js)是 javaScript(以下简称js) 运行时的一个环境.不是一门语言. 以 ...

  9. vue 快速入门 系列 —— 初步认识 vue

    其他章节请看: vue 快速入门 系列 初步认识 vue vue 是什么 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架. 所谓渐进式,就是你可以一步一步.有阶段 ...

随机推荐

  1. Iwpriv工作流程及常用命令使用之二

    iwpriv工具通过ioctl动态获取相应无线网卡驱动的private_args所有扩展参数 iwpriv是处理下面的wlan_private_args的所有扩展命令,iwpriv的实现上,是这样的, ...

  2. codeforces 682C Alyona and the Tree DFS

    这个题就是在dfs的过程中记录到根的前缀和,以及前缀和的最小值 #include <cstdio> #include <iostream> #include <ctime ...

  3. 《Python 学习手册4th》 第四章 介绍Python对象类型

    ''' 时间: 9月5日 - 9月30日 要求: 1. 书本内容总结归纳,整理在博客园笔记上传 2. 完成所有课后习题 注:“#” 后加的是备注内容(每天看42页内容,可以保证月底看完此书) ''' ...

  4. 常见设计模式解析和实现(C++)Adapt模式

    作用:将一个类的接口转换成客户希望的另一个接口.Adapt模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. UML示意图 1)      采用继承原有接口类的方式 2)采用组合原有接口类 ...

  5. JSP中字符编码转换问题

    问题描述:一个input.jsp页面中的参数,传递到另外一个save.jsp页面上,然后存入到数据库中,如果input.jsp页面输入偶数中文没有问题,输入奇数则出现?,存入数据库的也是?. 问题源码 ...

  6. bzoj 3365 [Usaco2004 Feb]Distance Statistics 路程统计(点分治,单调)

    [题意] 求树上长度不超过k的点对数目. [思路] 和 Tree 一样一样的. 就是最后统计的时候别忘把根加上. [代码] #include<set> #include<cmath& ...

  7. hbase使用-java操作

      .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courie ...

  8. 修改 jquery easyui 表单验证默认的样式

    目前对于不符合要求的输入域会在右侧显示一个带箭头的提示,可是如果我的输入框比较靠右的话就显示不全了(虽然会出滚动条,但是由于鼠标移开就消失了,所以还是看不到提示内容)! 能不能把这个提示的位置改变一下 ...

  9. c函数调用过程原理及函数栈帧分析

    转载自地址:http://blog.csdn.net/zsy2020314/article/details/9429707       今天突然想分析一下函数在相互调用过程中栈帧的变化,还是想尽量以比 ...

  10. linux下编译lua

    curl -R -O http://www.lua.org/ftp/lua-5.2.3.tar.gz 编译代码时,遇到如下错误 /usr/lib/libreadline.so: undefined r ...