一、问题描述

  如上图所示,有一个由非负整数组成的三角形,第一行只有一个数,除了最下行之外每个数的左下方和右下方各有一个数。现请你在此数字三角形中寻找一条从首行到最下行的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于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. 关于css透明度的问题

    先看background和background-color background:可以设置背景颜色,背景图片,还有定位.默认background:no-repeat; background-color ...

  2. 互联网高级Java面试总结

    前不久刚换了单位,这段时间抽出时间来总结一下. 本人渣本毕业四年,无大厂工作经验,出来面高级Java. 上家单位是一个知名互联网平台,但是体量不大的小公司(5线互联网公司),但就是出名(职场人都知道~ ...

  3. 小白袍 -- Chapter 1 Java中的Encode与Decode

    前几天做一个邮件发送功能,一些常用信息配置在properties文件中,通过prop.getProperty(key)来获取配置的信息,结果配置文件中是用中文写的,邮件发送成功后,邮箱中的激活链接是乱 ...

  4. mysql忘记密码重置

    一.更改my.cnf配置文件 0.MySQL 版本查看 mysql --version 1.用命令编辑/etc/my.cnf配置文件,即:vim /etc/my.cnf 或者 vi /etc/my.c ...

  5. SQL Server中的三种Join方式

      1.测试数据准备 参考:Sql Server中的表访问方式Table Scan, Index Scan, Index Seek 这篇博客中的实验数据准备.这两篇博客使用了相同的实验数据. 2.SQ ...

  6. React中需要多个倒计时的问题

    最近有一个需求是做一个闪购列表,列表中每一个商品都有倒计时,如果每一个倒计时都去生成一个setTimeout的话,一个页面就会有很多定时器,感觉这种做法不是非常好,于是换了一个思路. 思路是这样的,一 ...

  7. deepin15.7下使用apt安装mysql5.7不显示root密码设置的解决方法

    在安装MySQL的过程中,并没有要求设置root账户密码的步骤,导致很多人无法使用root账户登录 这个问题早已有解决方案,笔者在deepin15.7下安装也遇到同样问题,只是做一个简单的记录 解决思 ...

  8. nginx 只允许域名访问,禁止IP访问

    在nginx中为了防止,通过ip地址或者没有备案的域名代理到nginx上,可以在nginx中配置来阻止这一操作 #只可以用域名访问(此处的server是新增,并不是在原有的server基础上修改),默 ...

  9. python+matplotlib 绘制等高线

    python+matplotlib 绘制等高线 步骤有七: 有一个m*n维的矩阵(data),其元素的值代表高度 构造两个向量:x(1*n)和y(1*m).这两个向量用来构造网格坐标矩阵(网格坐标矩阵 ...

  10. Hadoop(8)-HDFS的读写数据流程以及机架感知

    1. HDFS的写数据流程 1.客户端通过fs模块向NameNode申请文件上传,NameNode检查请求是否合法,如用户权限,目标文件是否已存在,父目录是否存在等等 2.NameNode返回是否可以 ...