三角形最佳路径问题

GitHub跳转原题关系图返回列表

数字三角形经典 DP:自底向上滚动数组,O(h²) 时间求最大路径和。

OJ: roj

题目 ID: 1288

难度:入门

标签:动态规划数字三角形滚动数组

创建: 2026-09-30 03:26

更新: 2026-10-05 08:09

题目描述

给定一个高度为 hh(1⩽h⩽1001 \leqslant h \leqslant 100)的数字三角形,第 rr 行有 rr 个正整数。从顶部出发,每一步只能走到下一层正下方或右下方的数,直到最底层,求路径上数字之和的最大值。输入第一行为 hh;接下来 hh 行,第 rr 行为该行的 rr 个整数,用空格分隔;输出最佳路径的数字之和。例如输入 5,7,3 8,8 1 0,2 7 4 4,4 5 2 6 5 时输出 30。

思路

设 f(r,c)f(r, c) 表示从 (r,c)(r, c) 出发到底部的最大路径和,则 f(r,c)=a(r,c)+max⁡(f(r+1,c),f(r+1,c+1))f(r, c) = a(r, c) + \max(f(r+1, c), f(r+1, c+1)),最底层 f(h,c)=a(h,c)f(h, c) = a(h, c),答案为 f(1,1)f(1, 1)。递推只依赖下一行,用一维数组自底向上滚动即可,时间复杂度 O(h2)O(h^2)。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 03:27
# update_at: 2026-10-04 10:27

import sys


def solve() -> None:
    data = iter(map(int, sys.stdin.buffer.read().split()))
    n = next(data)  # 三角形高度,也是最底层一行里的数字个数

    tri: list[list[int]] = []
    for i in range(1, n + 1):
        tri.append([next(data) for _ in range(i)])  # 第 i 行共 i 个数

    dp = tri[-1][:]  # 最底层 dp 初值
    for r in range(n - 2, -1, -1):
        dp = [tri[r][j] + max(dp[j], dp[j + 1]) for j in range(r + 1)]

    print(dp[0])


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-05 08:09
 * update_at: 2026-10-05 08:09
 */

// 数字三角形:自底向上 DP,dp[j] 表示从当前行第 j 列出发到底部的最大路径和。

#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long ll;

const int MAXH = 105; // h <= 100

int h;             // 三角形高度
ll tri[MAXH][MAXH]; // tri[r][j] 表示第 r 行第 j 列的数字(下标从 1 开始)
ll dp[MAXH];        // 滚动数组:dp[j] = 从当前行第 j 列出发到底部的最大路径和

int main() {
    scanf("%d", &h);
    for (int i = 1; i <= h; i++)
        for (int j = 1; j <= i; j++)
            scanf("%lld", &tri[i][j]);

    // 初始化为最底层一行:从底层出发的最大和就是自身
    for (int j = 1; j <= h; j++) dp[j] = tri[h][j];

    // 自底向上递推:f(r,j) = a(r,j) + max(f(r+1,j), f(r+1,j+1))
    for (int i = h - 1; i >= 1; i--)
        for (int j = 1; j <= i; j++)
            dp[j] = tri[i][j] + max(dp[j], dp[j + 1]);

    printf("%lld\n", dp[1]);
    return 0;
}