2023-3-22 每日一题 数字三角形
文章目录
1. 题目描述
给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。
7 3 8 8 1 0 2 7 4 4 4 5 2 6 5
2. 输入格式
第一行包含整数 n,表示数字三角形的层数。
接下来 n行,每行包含若干整数,其中第 i 行表示数字三角形第 i 层包含的整数。
3. 输出格式
输出一个整数,表示最大的路径数字和。
4. 样例
4.1 输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
4.2 输出
30
5. 数据范围
1 <= n <= 500
−10000 <= 三角形中的整数 <= 10000
6. 题解
6.1 解题方法探寻
- 此题意为从顶部出发到达底部使得路径上的数字的和最大。但是只能走左下和右下。
- 我们发现这是一个多阶段的决策问题,那么我们就可以把多阶段转化为一系列单阶段问题,再利用各阶段的关系进行逐一求解。
- 显而易见,使用动态规划无疑是 best choice!
- 动态规划通常就是在递推公式以及一个或者多个初始状态的基础上,当前子问题的解由上一个子问题的解推出。
6.2 动态规划求解过程
- 拆分问题: 将问题拆分成一系列的子问题。此题是找最大路径数字和,那我们进行逆序求解,从最底层开始考虑,求解出从最底层出发不断向上遍历的最大路径数字和。
- 定义状态并找出初始状态: 定义dp数组,可以为一维数组也可为二维数组(由具体的题目需求而定),用于保存每一个子问题的结果。此题是定义二维数组,用于存储数字三角形每一个点的状态(即到达该点的最大路径数字和)。再由于我们是从底部出发,因此最底层原有的数字即为初始状态,需提将 dp 数组代表的最底层进行赋值。
- 写出状态转移方程: 由于从头出发的话只能走左下或者右下,那么从倒数第二层开始某个点对应的 dp 数组的值皆为该点左下角的值或者右下角的值 + 该点自己本身的值,由于题目要求的最大路径数字和,因此需对左下角或者右下角取最大值。则状态方程为 dp[i][j] = max(dp[i+1][j], dp[i+1][j+1]) + a[i][j]。
6.3 具体步骤如下:
- 定义整型变量 n,存储数字三角形的边长,并进行输入。定义时考虑数据范围。由于给定的数据范围不是很大,则使用 int 定义。
- 定义一个二维数组 a[1005][1005],用于存储数字之间三角形,并使用两重循环进行输入。
- 定义一个二维数组 dp[1005][1005],用于存储数字三角形每一个位置的状态(即 dp[i][j] 就代表着到达该点的最大路径数字和)。
- 找到初始条件,设置 dp 数组的边界值。即使用 for 循环对最后一层进行初始化。
- 再使用两重循环以及状态转移方程对状态进行逐层遍历即可,当每一层都遍历完(即已经从尾部走到头部),dp 数组的第一个元素的值即为求解结果。
6.4 上代码
#include<iostream>
using namespace std;
// 定义尽可能大,以防越界
int a[1005][1005];
int dp[1005][1005];
int main()
{
// 定义三角形的层数并进行输入
int n;
cin >> n;
// 输入数字直角三角形
for(int i = 1; i <= n; i++)
for(int j = 1; j <= i; j++)
cin >> a[i][j];
// 对最后一层进行初始化赋值
for(int i = 1; i <= n; i++)
dp[n][i] = a[n][i];
// 使用循环遍历每一层并使用状态转移方程求解 dp 数组的值
for(int i = n-1; i >= 1; i--)
for(int j = 1; j <= i; j++)
dp[i][j] = max(dp[i+1][j], dp[i+1][j+1]) + a[i][j];
// 由于下标从 1 开始,所以输出 dp[1][1]
cout << dp[1][1];
return 0;
}