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在经历了螃蟹先生的任务之后被奖励了一次出国旅游的机会,于是他 ...
随机推荐
- Vue node.js商城-购物车模块
一.渲染购物车列表页面 新建src/views/Cart.vue获取cartList购物车列表数据就可以在页面中渲染出该用户的购物车列表数据 data(){ return { car ...
- PL/SQL语句快捷输入设置
设置PL/SQL语句快捷输入的方法,让你成为高效率的人. 1.打开PL/SQL,输入用户并登录 2.并打开Tools->Preferences->Editor->AutoReplac ...
- LINUX日志的错误等级
LINUX日志的错误等级 - CSDN博客 https://blog.csdn.net/solmyr_biti/article/details/50634533讯息等级 系统将讯息分为七个主要的等级, ...
- Sonar安装-Linux[20171227]
前言 一款不错的代码质量管理工具Sonar 前期准备 官方参考文档 https://docs.sonarqube.org/display/SONAR/Documentation ...
- XAMPP中的MySQL与本地MySQL冲突的问题
学习SQL时在本地中先安装了MySQL,后来因为项目需要又安装了XAMPP集成环境,今天在启动项目的时候发现启动MySQL各种问题(期望启动的是XAMPP中的MySQL服务),在Navicat中显示成 ...
- 大数据开发从入门小白到删库跑路(一)- 获取Hadoop
Hadoop是一个可以通过相对简单编程模型实现跨多台计算机集群分布式处理大型数据集的框架.它不是依赖于高额成本的硬件可靠性来提供高可用性,Hadoop的设计能从单个服务器扩展到数千台机器,每个机器提供 ...
- Java反射的两种使用方法
1.创建User.java package com.tao.test; public class User { private String name; private int id; public ...
- 【ospf-路由聚合】
- ctf题目writeup(3)
题目地址: https://www.ichunqiu.com/battalion 1. 这个是个mp3,给的校验是为了下载下来的. 下来之后丢进audicity中 放大后根据那个音块的宽度来确定是 . ...
- spoj1026 favorite dice
#include <bits/stdc++.h> using namespace std; int n,t; ; double dp[N]; /* 甩一个n面的骰子,问每一面都被甩到的需要 ...