AcWing898.数字三角形
题目链接:AcWing898.数字三角形
1. 题目描述
给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输入格式
第一行包含整数n,表示数字三角形的层数。
接下来n行,每行包含若干整数,其中第 i 行表示数字三角形第 i 层包含的整数。
输出格式
输出一个整数,表示最大的路径数字和。
数据范围
1
≤
n
≤
500
1≤n≤500
1≤n≤500,
−
10000
≤
三
角
形
中
的
整
数
≤
10000
−10000 ≤ 三角形中的整数≤10000
−10000≤三角形中的整数≤10000
输入样例:
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出样例:
30
2. 解题思路
首先,对于题目中的任意一点,都有很多种走法。说明,这个题目中,每个结点对应的路径值都会包含能走到该结点的前一个结点路径值,即从
(
1
,
1
)
→
(
i
,
j
)
(1,1) \rightarrow (i,j)
(1,1)→(i,j)一定包含
(
1
,
1
)
→
(
i
−
1
,
j
)
(1,1) \rightarrow (i - 1, j)
(1,1)→(i−1,j),所以在计算时,如果使用暴力破解的方法,会使本题的复杂度变成指数级,所以使用动态规划算法可以降低时间复杂度。
D
P
{
状
态
表
示
f
(
i
,
j
)
{
集
合
:
所
有
从
(
1
,
1
)
走
到
(
i
,
j
)
走
法
的
集
合
属
性
:
m
a
x
(
路
径
上
权
值
的
最
大
值
)
状
态
计
算
:
对
于
(
i
,
j
)
可
以
从
(
i
−
1
,
j
)
和
(
i
−
1
,
j
−
1
)
转
移
过
来
,
即
(
1
,
1
)
→
(
i
−
1
,
j
)
→
(
i
,
j
)
,
前
一
步
部
分
正
好
表
示
的
是
f
(
i
−
1
,
j
)
,
同
理
(
i
−
1
,
j
−
1
)
这
个
点
可
以
表
示
为
f
(
i
−
1
,
j
−
1
)
。
两
者
取
一
个
m
a
x
,
再
加
上
w
(
i
,
j
)
的
值
即
可
得
到
f
(
i
,
j
)
的
结
果
。
DP\begin{cases} 状态表示 f(i,j) \begin{cases} 集合:所有从(1, 1)走到(i, j)走法的集合 \\ \\ 属性:max (路径上权值的最大值) \end{cases}\\ \\ 状态计算: 对于(i, j)可以从(i - 1, j)和(i - 1, j - 1)转移过来,\\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 即(1, 1) \rightarrow (i - 1, j) \rightarrow (i, j), 前一步部分正好表示的是f(i - 1, j), \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 同理(i - 1, j - 1)这个点可以表示为f(i - 1, j - 1)。\\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 两者取一个max,再加上w(i, j)的值即可得到f(i, j)的结果。 \end{cases}
DP⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧状态表示f(i,j)⎩⎪⎨⎪⎧集合:所有从(1,1)走到(i,j)走法的集合属性:max(路径上权值的最大值)状态计算:对于(i,j)可以从(i−1,j)和(i−1,j−1)转移过来, 即(1,1)→(i−1,j)→(i,j),前一步部分正好表示的是f(i−1,j), 同理(i−1,j−1)这个点可以表示为f(i−1,j−1)。 两者取一个max,再加上w(i,j)的值即可得到f(i,j)的结果。
最后答案是
max
1
≤
i
≤
n
{
f
(
n
,
i
)
}
\max_{1 \leq i \leq n}\{f(n, i)\}
max1≤i≤n{f(n,i)}
3. 代码实现
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 510;
int n;
int w[N][N];
int f[N][N];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= i; j ++ )
cin >> w[i][j];
memset(f, -0x3f, sizeof f);
f[1][1] = w[1][1];
for (int i = 2; i <= n; i ++ )
for (int j = 1; j <= i; j ++ )
f[i][j] = max(f[i - 1][j], f[i - 1][j - 1]) + w[i][j];
int res = -0x3f3f3f3f;
for (int i = 1; i <= n; i ++ )
res = max(res, f[n][i]);
printf("%d\n", res);
return 0;
}
4. 注意事项
- 初始化
因为,本题状态更新时,在第一列会出现非法状态向合法状态转移的情况,所以要注意数组的初始化。由于本题有负数,所以只需要将数组中所有元素初始化成负无穷即可(也可以单独初始化)。 - 一定注意在求
res
这个结果时,要初始化成负无穷,否则无法求出正确的值。