在问 · 算法题解

120. 三角形最小路径和

中等动态规划自底向上一维 DP力扣原题 ↗

题目描述

输入一个三角形数组 triangle,第 i 行(从 0 开始)有 i + 1 个整数。

从顶端出发,每次走到下一行,直到最后一行。若当前位于某行下标 j 处,下一步只能去下一行的下标 j 或 j + 1。

路径和是经过的所有数之和。返回所有可能路径中最小的路径和。

示例 1
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11

解释:沿 2 -> 3 -> 5 -> 1 走,和为 11,是最小值。

示例 2
输入:triangle = [[-10]]
输出:-10

解释:只有一个数,路径和就是 -10。

数据范围

  • 1 <= triangle.length <= 200
  • triangle[0].length == 1
  • triangle[i].length == triangle[i - 1].length + 1
  • -10^4 <= triangle[i][j] <= 10^4

进阶:能否只用 O(n) 的额外空间完成,其中 n 是三角形的行数?

解题思路:自底向上一维 DP

自底向上。设 dp[j] 为从当前行第 j 个往下走到底的最小和,初始为最后一行。从倒数第二行向上:dp[j] = triangle[i][j] + min(dp[j], dp[j+1])。最后 dp[0] 就是从顶端出发的答案。

为什么正确

处理完第 i 行后,dp[0..i] 是第 i 行各位置到底部的最小和。

复杂度分析

时间 O(n²),额外空间 O(n)。

易错点

自顶向下需要处理每行两端的边界,最后还要在整行中取最小值;自底向上没有这些边界问题。贪心地每步选较小的子节点是错的。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        dp = triangle[-1][:]
        for i in range(len(triangle) - 2, -1, -1):
            for j in range(i + 1):
                dp[j] = triangle[i][j] + min(dp[j], dp[j + 1])
        return dp[0]

载入编辑器,自己跑一遍 →

其他解法

两者状态定义相同;记忆化搜索是自顶向下的写法,主解法按行自底向上迭代,并把空间压到 O(n)。

解法 2:记忆化搜索 时间 O(n²),空间 O(n²)

dfs(i, j) 表示从第 i 行第 j 个数出发走到底部的最小路径和。最后一行直接返回自身;否则等于 triangle[i][j] 加上 dfs(i+1, j) 和 dfs(i+1, j+1) 的较小值。用二维表缓存结果,每个状态只算一次。

直接由递归定义得到,最容易想到。缓存表占 O(n²) 空间,还有递归开销;不加缓存会退化成 O(2ⁿ)。

from typing import List
from functools import lru_cache

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        n = len(triangle)

        @lru_cache(maxsize=None)
        def dfs(i, j):
            if i == n - 1:
                return triangle[i][j]
            return triangle[i][j] + min(dfs(i + 1, j), dfs(i + 1, j + 1))

        return dfs(0, 0)

动态规划 专题的其他题目

查看 动态规划 全部题目 →