一、问题描述

  如上图所示,有一个由非负整数组成的三角形,第一行只有一个数,除了最下行之外每个数的左下方和右下方各有一个数。现请你在此数字三角形中寻找一条从首行到最下行的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为 0 - 99 。

二、问题分析

  要求找出一条路径,它经过的数字之和最大。我们可以细化到每一步,每一次往下走都要选择较大的数。

  于是可以得出下面的以下的伪代码:

if(当前行是最下行)
当前行到最下行的最大和 = 当前数字的值
else
当前行到最下行的最大和 = 下一行到最下行的最大和 + 当前数字的值

  这样的伪代码实在是难读,因此我们需要用抽象的方法思考问题(即变量化):

  • (i , j):当前的位置(状态)
  • a(i , j):表示第 i 行的第 j 个数字          //i , j > 0
  • d(i , j):从位置(i , j)到最下行的最大和    //状态(i , j)的指标函数,且原问题的解是d(1,1)

  于是,上面的伪代码转化为:

if(i == n)
d(i,j) = a(i,j);
else
d(i,j) = max{d(i+1,j),d(i+1,j+1)} + a(i,j);

  我们来看不同的状态之间是怎么转移的:从位置(i , j)出发有两种决策,①往左走,则走到(i+1 , j)后,将要求解d(i+1 , j);②往右走,则走到(i+1 , j+1)后,将要求解d(i+1 , j+1)。

  由于可以在这两个决策中自由选择,所以应选择d(i+1 , j)和d(i+1 , j+1)中较大的那个。这一步正导出了所谓的状态转移方程:

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

  这个方程已经蕴含了最优质结构性质(全局最优解包含局部最优解)。即如果连“从(i+1 , j)或(i+1 , j+1)出发到最下行”这部分的和都不是最大的,加上a(i , j)之后肯定也不是最大的。

三、解题方式

1. 递归计算

int solve(int i,int j)
{
if(i == n) return a[i][j];
else return max(solve(i+1,j),solve(i+1,j+1)) + a[i][j];
}

  分析:用直接递归的方法计算状态转移方程,效率往往十分低下。其原因是相同的子问题被重复计算。

2. 递推计算

for(int j=1;j<=n;j++)	d[n][j] = a[n][j];		//最后一行
for(int i=n-1;i>=1;i--)
for(int j=1;j<=i;j++)
d[i][j] = max(d[i+1][j],d[i+1][j+1]) + a[i][j];

  分析:i 是逆序枚举的,所以在计算d[i][j]前,它所需要的d[i+1][j]和d[i+1][j+1]都已经计算出来了。

  提示:可以用递推法计算状态转移方程,递推的关键是边界和计算顺序。

3. 记忆化搜索

/*	第一部分:将d全部初始化为-1	*/
memset(d,-1,sizeof(d));
/* 第二部分:编写递归函数 */
int solve(int i,int j)
{
if(d[i][j] != -1) return d[i][j];     //判断状态(i,j)是否已经被计算过
if(i == n) return d[i][j] = a[i][j];
else return d[i][j] = max(solve(i+1,j),solve(i+1,j+1)) + a[i][j];
}

  分析:此程序是递归的,但是它同时把计算结果保存在数组d中。所以,千万别忘记在计算之后把它保存在d[i][j]中。此程序的方法称为记忆化,它虽然不像递推法那样显式地指明了计算顺序,但仍然可以保证每个结点只访问一次。

  提示:根据C语言“赋值语句本身有返回值”的规定,可以把保存d[i][j]的工作合并到函数的返回语句中。

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

四、解题代码

1. 递归计算

【第一次错误代码】

 #include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstdlib>
using namespace std; int n;
int a[][]; int solve(int i,int j)
{
if(i==n) return a[i][j];
else return a[i][j] + max(a[i+][j],a[i+][j+]); //error
} int main()
{
cin>>n;
for(int i=;i<=n;i++){
for(int j=;j<=i;j++){
scanf("%d",&a[i][j]);
}
}
int ans = solve(,);
printf("%d\n",ans);
return ;
}

【第二次正确代码】

 #include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstdlib>
using namespace std; int n;
int a[][]; int solve(int i,int j)
{
if(i==n) return a[i][j];
else return a[i][j] + max(solve(i+,j),solve(i+,j+));
} int main()
{
cin>>n;
for(int i=;i<=n;i++){
for(int j=;j<=i;j++){
scanf("%d",&a[i][j]);
}
}
int ans = solve(,);
printf("%d\n",ans);
return ;
}
  • 分析:提交到poj1163显示TLE,显然递归求解不可行!

2. 记忆化搜索

 #include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstdlib>
using namespace std; int n;
int a[][];
int d[][]; int solve(int i,int j)
{
if(d[i][j] != -) return d[i][j];
if(i==n) return d[i][j] = a[i][j];
else return d[i][j] = a[i][j] + max(solve(i+,j),solve(i+,j+));
} int main()
{
cin>>n;
for(int i=;i<=n;i++){
for(int j=;j<=i;j++){
scanf("%d",&a[i][j]);
}
}
memset(d,-,sizeof(d));
int ans = solve(,);
printf("%d\n",ans);
return ;
}

3. 递推计算

 #include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstdlib>
using namespace std; int n;
int a[][];
int d[][]; int main()
{
cin>>n;
for(int i=;i<=n;i++){
for(int j=;j<=i;j++){
scanf("%d",&a[i][j]);
}
}
for(int j=;j<=n;j++) d[n][j] = a[n][j];
for(int i=n-;i>=;i--){
for(int j=;j<=i;j++){
d[i][j] = a[i][j] + max(d[i+][j],d[i+][j+]);
}
}
printf("%d\n",d[][]);
return ;
}
  • 小结:这方面的代码还不够熟练,继续加强!

DP入门(1)——数字三角形问题的更多相关文章

  1. dp递推 数字三角形,dp初学者概念总结

    数字三角形(POJ1163)          在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大.路径上的每一步都只能往左下或 右下走.只需要求出这个最大和即可,不必给出 ...

  2. 数字三角形 (DP入门)

    7 3     8 8     1     0 2     7     4     4  4     5     2     6     5 给出一个数字三角形.从三角形的顶部到底部有很多条不同的路径 ...

  3. 简单DP入门(一) 数字三角形

    数字三角形

  4. 4829 [DP]数字三角形升级版

    4829 [DP]数字三角形升级版  时间限制: 1 s  空间限制: 16000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 从数字三角形的顶部(如图, ...

  5. xbz分组题B 吉利数字 数位dp入门

    B吉利数字时限:1s [题目描述]算卦大湿biboyouyun最近得出一个神奇的结论,如果一个数字,它的各个数位相加能够被10整除,则称它为吉利数.现在叫你计算某个区间内有多少个吉利数字. [输入]第 ...

  6. HDU 1176 免费馅饼 (类似数字三角形的题,很经典,值得仔细理解的dp思维)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1176 免费馅饼 Time Limit: 2000/1000 MS (Java/Others)     ...

  7. C++数字三角形问题与dp算法

    题目:数字三角形 题目介绍:如图所示的数字三角形,要求从最上方顶点开始一步一步下到最底层,每一步必须下一层,求出所经过的数字的最大和. 输入:第一行值n,代表n行数值:后面的n行数据代表每一行的数字. ...

  8. 算法训练 数字三角形(DP)

    问题描述 (图3.1-1)示出了一个数字三角形. 请编一个程序计算从顶至底的某处的一条路 径,使该路径所经过的数字的总和最大. ●每一步可沿左斜线向下或右斜线向下走: ●1<三角形行数≤100: ...

  9. hihoCoder#1037 : 数字三角形(DP)

    [题目链接]:click here~~ 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 问题描写叙述 小Hi和小Ho在经历了螃蟹先生的任务之后被奖励了一次出国旅游的机会,于是他 ...

随机推荐

  1. vi常用命令学习

    (1)移动光标 h : 左移光标l : 右移光标j : 下移光标k : 上移光标 w : 移动到下一个单词词头b : 移动到上一个单词词头e : 移动到本单词的尾部 0 :移动到当前行的开端$ :移动 ...

  2. Oracle数据库中 to_date()与to_char()函数的用法

    to_date() ,to_char()与24小时制表示法及mm分钟的显示: 一.在使用Oracle的to_date函数来做日期转换时,很多Java程序员也许会直接的采用“yyyy-MM-dd HH: ...

  3. leetcode笔记(四)9. Palindrome Number

    题目描述 Determine whether an integer is a palindrome. An integer is a palindrome when it reads the same ...

  4. ABAP术语-ABAP Workbench

    ABAP Workbench 原文:http://www.cnblogs.com/qiangsheng/archive/2007/12/10/989037.html Integrated graphi ...

  5. 使用php+gmail 发送邮件

    <?php namespace app\index\controller; use think\Controller; use PHPMailer\PHPMailer; class Test e ...

  6. MySQL数据库操作(DDL)

    一.创建数据库 语法:create database 数据库名称 [库选项]; 库选项:(可选)数据库的属性,一般有字符集与校对集,保存在数据库所属文件夹下的opt文件 charset:字符集,表示该 ...

  7. Spring笔记1

    Spring Spring特点 1. 方便解耦,简化开发 通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合.有了Spring,用户 ...

  8. discuzX3.2 X3.4网站漏洞修复 SQL注入与请求伪造攻击利用与修复

    2018年12月9日,国内某安全组织,对discuz X3.2 X3.4版本的漏洞进行了公开,这次漏洞影响范围较大,具体漏洞是discuz 的用户前段SQL注入与请求伪造漏洞,也俗称SSRF漏洞,漏洞 ...

  9. WHERE条件中or与union引起的全表扫描的问题

    说起数据库的SQL语句执行效率的问题,就不得不提where条件语句中的or(逻辑或)引起的全表扫描问题,从而导致效率下降. 在以往绝大多数的资料中,大多数人的建议是使用 union 代替 or ,以解 ...

  10. uva 253 - Cube painting(相同骰子)

    习题4-4 骰子涂色(Cube painting, UVa 253) 输入两个骰子,判断二者是否等价.每个骰子用6个字母表示,如图4-7所示. 图4-7 骰子涂色 例如rbgggr和rggbgr分别表 ...