DP入门(1)——数字三角形问题
一、问题描述
如上图所示,有一个由非负整数组成的三角形,第一行只有一个数,除了最下行之外每个数的左下方和右下方各有一个数。现请你在此数字三角形中寻找一条从首行到最下行的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于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)——数字三角形问题的更多相关文章
- dp递推 数字三角形,dp初学者概念总结
数字三角形(POJ1163) 在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大.路径上的每一步都只能往左下或 右下走.只需要求出这个最大和即可,不必给出 ...
- 数字三角形 (DP入门)
7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 给出一个数字三角形.从三角形的顶部到底部有很多条不同的路径 ...
- 简单DP入门(一) 数字三角形
数字三角形
- 4829 [DP]数字三角形升级版
4829 [DP]数字三角形升级版 时间限制: 1 s 空间限制: 16000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 从数字三角形的顶部(如图, ...
- xbz分组题B 吉利数字 数位dp入门
B吉利数字时限:1s [题目描述]算卦大湿biboyouyun最近得出一个神奇的结论,如果一个数字,它的各个数位相加能够被10整除,则称它为吉利数.现在叫你计算某个区间内有多少个吉利数字. [输入]第 ...
- HDU 1176 免费馅饼 (类似数字三角形的题,很经典,值得仔细理解的dp思维)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1176 免费馅饼 Time Limit: 2000/1000 MS (Java/Others) ...
- C++数字三角形问题与dp算法
题目:数字三角形 题目介绍:如图所示的数字三角形,要求从最上方顶点开始一步一步下到最底层,每一步必须下一层,求出所经过的数字的最大和. 输入:第一行值n,代表n行数值:后面的n行数据代表每一行的数字. ...
- 算法训练 数字三角形(DP)
问题描述 (图3.1-1)示出了一个数字三角形. 请编一个程序计算从顶至底的某处的一条路 径,使该路径所经过的数字的总和最大. ●每一步可沿左斜线向下或右斜线向下走: ●1<三角形行数≤100: ...
- hihoCoder#1037 : 数字三角形(DP)
[题目链接]:click here~~ 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 问题描写叙述 小Hi和小Ho在经历了螃蟹先生的任务之后被奖励了一次出国旅游的机会,于是他 ...
随机推荐
- IPv6静态路由、动态路由
实验涉及命令以及知识补充 IPv6 接口必须配置 IPv6 地址和子网掩码 使用 ipv6 address ipv6-address/prefix-length [link-local | eui-6 ...
- 访问oracle数据库
如果是本地 :sqlplus system/xxx(用户名/密码) 远程到服务器, sqlplus system/xxx(用户名/密码) @IP:port/orcl(orcl为数据库实例)
- ajax 全局拦载处理,可加密、过滤、筛选、sql防注入处理
//此方法放在公用的js里面即可.如此:所有的ajax请求都会通过此 $.ajaxSetup({ contentType: "application/x-www-form-urlencode ...
- 【HDOJ 1269】迷宫城堡(tarjan模板题)
Problem Description 为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若 ...
- 背景qwq
- nigx配置location规则
以下是收集的,对于不是很熟的朋友,配置转发很有帮助 1.location匹配命令和优先级: 优先级: Directives with the = prefix that match the query ...
- jsonp 跨域只能调用一次ajax(无法多次调用或者循环调用)
jsonp 跨域只能掉用一次ajax(无法多次调用或者循环调用) 百度搜索关键字:jsonp 只能调用一次ajax 解决方法 //回调函数设置,给后台执行 window[callback ...
- vue入门笔记
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不仅易于上手,还便于与 ...
- Hive优化之谓词下推
Hive优化之谓词下推 解释 Hive谓词下推(Predicate pushdown) 关系型数据库借鉴而来,关系型数据中谓词下推到外部数据库用以减少数据传输 基本思想:尽可能早的处理表达式 属于逻辑 ...
- Python爬虫爬取百度翻译之数据提取方法json
工具:Python 3.6.5.PyCharm开发工具.Windows 10 操作系统 说明:本例为实现输入中文翻译为英文的小程序,适合Python爬虫的初学者一起学习,感兴趣的可以做英文翻译为中文的 ...