题目描述
思路解析
一句话答案:LeetCode 120 三角形最小路径和:自底向上动态规划 dp[j]=triangle[i][j]+min(dp[j],dp[j+1]),从底行往上填每格恰有两条来路,省掉自顶向下的边界特判,一维滚动,时间 O(n²)、空间 O(n)。
三角形最小路径和到底在求什么
给一个数字三角形 triangle,从顶上出发,每步只能走到下一行紧挨着的两个数之一——正下方或右下方,走到最底行,求这一路数字之和最小。以 [[2],[3,4],[6,5,7],[4,1,8,3]] 为例,走 2→3→5→1 得 11,是所有走法里最小的,答案就是 11。
为什么不能把每条路径都走一遍
最直接的想法是把每条路径都走一遍比出最小的。但每往下一行都要二选一,n 行三角形有约 2 的 n-1 次方条路径,指数级膨胀、枚举不完。慢在同一个格子被反复经过,往下那段路在无数条路径里一遍遍重算。既然每个格子往下的最优走法固定,算一次存下来复用,这就是动态规划——算过的子问题记下复用、不再重算。
为什么反过来从最底行往上填
不从顶往下算,而是从最底行倒着往上推。定义 dp[j] 为「当前这一行第 j 个格子往下走到底的最小路径和」。最底行的格子已经到底,最小和就是自己。往上一行行覆盖,算上面某格时它下面正铺着刚算完的一行。
好处是每个非底行的格子都规整地有正下方、右下方两条来路。若从顶往下填,每行头尾只有一条来路(贴着斜边),要单独写边界判断;倒着从底往上,这类首尾特判整个消失。
本格加下方两格较小,为什么能压成一行
转移式(由已算好的格子推出当前格子的公式)就一句:dp[j] = triangle[i][j] + min(dp[j], dp[j+1]),本格值加上正下方、右下方里更小的那个。下标别弄反:正下方是 dp[j]、右下方是 dp[j+1],同一个 j 差一位。
算当前行第 j 格只用到下一行的 dp[j] 和 dp[j+1],再往下的行用不上了。所以不必存整张二维表,只需一个长度等于行数的一维数组,从底行起每算一行就地覆盖旧值。这种把二维表滚成一行的写法叫滚动数组,空间从 O(n²) 压到 O(n)。
拿这个三角形把每一格亲手填一遍
拿 [[2],[3,4],[6,5,7],[4,1,8,3]] 走一遍。最底行 [4,1,8,3] 各自到底,dp 起始就是 4、1、8、3。往上一行 [6,5,7]:6 的下方是 4 和 1 取 1,记 6+1=7;5 的下方是 1 和 8 取 1,记 5+1=6;7 的下方是 8 和 3 取 3,记 7+3=10。这行 dp 是 7、6、10。
再上一行 [3,4]:3 的下方是 7 和 6 取 6,记 3+6=9;4 的下方是 6 和 10 取 6,记 4+6=10,dp 是 9、10。顶行 [2]:2 的下方是 9 和 10 取 9,记 2+9=11。填到顶格得 11,就是这个三角形的最小路径和。
复杂度是多少,两个下标和答案位置别搞错
三角形约 n²/2 个格子,每格一次取小、一次加法都是常数,时间 O(n²);一维滚动只留一行,空间 O(n)。
两处最容易出错。一是下标:正下方 dp[j]、右下方 dp[j+1],写成 dp[j-1] 或取反就顺着错路加。二是答案位置:填完后答案是顶格 dp[0],它汇总了全部路径,不是最底行里最小的数——底行的值只是各自起点。数带负号也照样成立。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记住这条「本格 + 下方两格较小」,下面每格都在套它。
先把三角形左对齐摆进表里(右边空位是三角形外,没有格子)。我们从最底一行开始往上填。
先看最底行第 0 格:它已经到底,没有下一步可走。
所以它的最小路径和就是自己 = 4。
先看最底行第 1 格:它已经到底,没有下一步可走。
所以它的最小路径和就是自己 = 1。
先看最底行第 2 格:它已经到底,没有下一步可走。
所以它的最小路径和就是自己 = 8。
先看最底行第 3 格:它已经到底,没有下一步可走。
所以它的最小路径和就是自己 = 3。
算第 2 行第 0 格:本格 6,下方两条路是 4 和 1,挑更小的 1。
6 + 1 = 7,填进第 2 行第 0 格。走「右下方」更省。
算第 2 行第 1 格:本格 5,下方两条路是 1 和 8,挑更小的 1。
5 + 1 = 6,填进第 2 行第 1 格。走「正下方」更省。
算第 2 行第 2 格:本格 7,下方两条路是 8 和 3,挑更小的 3。
7 + 3 = 10,填进第 2 行第 2 格。走「右下方」更省。
算第 1 行第 0 格:本格 3,下方两条路是 7 和 6,挑更小的 6。
3 + 6 = 9,填进第 1 行第 0 格。走「右下方」更省。
算第 1 行第 1 格:本格 4,下方两条路是 6 和 10,挑更小的 6。
4 + 6 = 10,填进第 1 行第 1 格。走「正下方」更省。
算第 0 行第 0 格:本格 2,下方两条路是 9 和 10,挑更小的 9。
2 + 9 = 11,填进第 0 行第 0 格。走「正下方」更省。
填到顶格 dp[0][0] = 11,这就是从顶到底的最小路径和。
边界先想清,负数同样适用。
两个高频追问。
参考代码
def minimumTotal(triangle): 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²),三角形共 n²/2 个格,每格 O(1)
- 空间:O(n),滚动一维 dp,长度 = 行数
易错点
面试追问把动画讲成自己的话
追问为什么自底向上比自顶向下好写?
追问空间能压到多少?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
乘积最大子数组
LeetCode 152 · 中等 · 沿着 动态规划套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题