数字三角形(数塔问题)


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

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 解决方法:记忆化搜索与递推


方法一:递归计算

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

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



方法二: 递推计算


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

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

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


方法三:记忆化搜索

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

依然是递归函数,同时把计算结果存在数组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. FTP文件上传与下载

    实现FTP文件上传与下载可以通过以下两种种方式实现(不知道还有没有其他方式),分别为:1.通过JDK自带的API实现:2.通过Apache提供的API是实现. 第一种方式:使用jdk中的ftpClie ...

  2. Linux基本命令(4)有关关机和查看系统信息的命令

    有关关机和查看系统信息的命令 命令 说明 shutdown 正常关机 reboot 重启计算机 ps 查看目前程序执行的情况 top 查看目前程序执行的情景和内存使用的情况 kill 终止一个进程 d ...

  3. 单片机usb转串口的时灵时不灵的解答

    写这篇博客,首先检讨一下自己,因为以前串口的程序,也和步进电机一样,时灵时不灵,我现在终于知道这是为什么了,因为51上有三个串口,一个公口,一个母口,一个usb转串口,这样的话,串口有3个了,我手头上 ...

  4. 微信公众平台开发(57)Emoji表情符号

    微信公众平台开发 微信公众平台开发模式 企业微信公众平台 Emoji表情符号 作者:方倍工作室 地址:http://www.cnblogs.com/txw1958/p/crack-golden-egg ...

  5. 使用logback.xml配置来实现日志文件输出

    转自:http://sungang-1120.iteye.com/blog/2104296 Logback是由log4j创始人设计的又一个开源日志组件.logback当前分成三个模块:logback- ...

  6. Java设计模式系列之工厂模式

    工厂模式将大量有共同接口的类实例化,工厂模式可以实现动态决定实例化哪一个类的对象,工厂模式在<Java与模式>中分为三类:1)简单工厂模式(Simple Factory):添加某一种类型的 ...

  7. CAAnimation解读

    序言 CAAnimation是一个抽象类,遵循了CAMediaTiming协议和CAAction协议!我们不要直接使用CAAnimation类,而是使用其子类: CATransition:提供渐变效果 ...

  8. POJ1189钉子和小球(DP)

    对钉子DP,如果钉子存在DP[i+1][j]+=DP[i][j]; DP[i+1][j+1]+=DP[i][j]; 如果不存在DP[i+2][j+1]+=4*DP[i][j]; 见代码:(有一个比较坑 ...

  9. CodeForces 732B Cormen — The Best Friend Of a Man (贪心)

    题意:给定n和k表示,狗要在任意连续两天散步次数要至少为k,然后就是n个数,表示每天的时间,让你增加最少次数使得这个条件成立. 析:贪心,策略是从开始到最后暴力,每次和前面一个相比,如果相加不够k,那 ...

  10. Windows xp下IDT Hook和GDT的学习

    一.前言   对于IDT第一次的认知是int 2e ,在系统调用的时候原来R3进入R0的方式就是通过int 2e自陷进入内核,然后进入KiSystemService函数,在根据系统服务调用号调用系统服 ...