动态规划:数字三角形问题
看完动态规划(DP)的“数字三角形”经典题,感觉终于摸到了DP的门
一、数字三角形问题背景
先说明题目:有一个由数字组成的三角形,从顶部出发,每次只能向下或向右下走,求走到底部时的路径数字和最大值。
举个例子(三角形如下):
3
7 4
2 4 6
8 5 9 3
最优路径是 3→7→4→9,和为23。
二、动态规划求解数字三角形的步骤
1.1 递归方程式(最优子结构+边界条件)
动态规划的核心是把原问题拆成子问题,用子问题的最优解推原问题的最优解(这就是“最优子结构”)。
定义状态
设 dp[i][j] 表示“从顶部走到第i行、第j列时的最大路径和”(行、列从1开始数,方便对应三角形结构)。
递归方程式
对于第i行第j列的位置,只能从上方(i-1行j列) 或 左上方(i-1行j-1列) 走过来,所以:
dp[i][j] = triangle[i][j] + max(dp[i-1][j-1], dp[i-1][j])
解释:当前位置的最大和 = 自身数字 + 上方/左上方中较大的路径和。
边界条件
• 三角形的第一行只有一个元素:dp[1][1] = triangle[1][1](顶部起点)
• 三角形的每一行的第一个元素(j=1):只能从上方(i-1行1列)走过来,所以 dp[i][1] = triangle[i][1] + dp[i-1][1]
• 三角形的每一行的最后一个元素(j=i):只能从左上方(i-1行i-1列)走过来,所以 dp[i][i] = triangle[i][i] + dp[i-1][i-1]
1.2 填表法的细节(维度、范围、顺序)
动态规划常用“填表”来存子问题的解,避免重复计算。
表的维度
三角形有n行,所以用二维数组dp[n][n](n是三角形的行数)。
填表范围
• 行范围:从第1行到第n行
• 列范围:第i行的列范围是1到i(因为第i行有i个元素)
填表顺序
必须从第1行开始,逐行向下填(因为计算第i行的dp值,需要先知道第i-1行的结果)。
以开头的例子(n=4)为例,填表过程:
-
第1行:dp[1][1] = 3
-
第2行:
◦ dp[2][1] = 7 + dp[1][1] = 10
◦ dp[2][2] = 4 + dp[1][1] = 7
- 第3行:
◦ dp[3][1] = 2 + dp[2][1] = 12
◦ dp[3][2] = 4 + max(dp[2][1], dp[2][2]) = 4+10=14
◦ dp[3][3] = 6 + dp[2][2] = 13
- 第4行:
◦ dp[4][1] = 8 + dp[3][1] = 20
◦ dp[4][2] = 5 + max(dp[3][1], dp[3][2]) = 5+14=19
◦ dp[4][3] = 9 + max(dp[3][2], dp[3][3]) = 9+14=23
◦ dp[4][4] = 3 + dp[3][3] = 16
原问题的最优值
填完表后,第n行的所有dp[n][j](j=1到n)中的最大值,就是整个问题的最优解。
比如例子中第4行的最大值是dp[4][3]=23,对应最优路径和。
1.3 时间和空间复杂度
• 时间复杂度:需要填n行,第i行填i个元素,总操作次数是1+2+...+n = n(n+1)/2,所以是O(n²)。
• 空间复杂度:用了二维数组dp[n][n],所以是O(n²)(也可以优化成O(n),用一维数组存当前行的结果,不过新手先记基础版~)
三、我对动态规划的理解和体会
作为算法小白,刚开始学DP的时候总觉得“绕”,但做完数字三角形,突然有了点感觉:
-
DP不是“直接算答案”,是“存子问题的解”:
以前做题习惯“从起点直接找终点”,但DP是先把“小问题”的答案存起来,比如先算“走到第2行的最大和”,再用它算“走到第3行的最大和”,最后拼出原问题的解——有点像“搭积木”。 -
状态定义是核心:
刚开始卡了很久,后来发现“怎么定义dp[i][j]”直接决定了能不能写出递归式。比如这题如果定义成“从第i行j列走到底部的最大和”,递归式会反过来,但核心还是“子问题的最优解”。 -
避免重复计算是DP的优势:
比如数字三角形如果用暴力递归,会重复计算很多路径(比如走到第3行第2列,会重复算走到第2行第1/2列的情况),但DP填表只算一次,效率高很多。
DP入门难,但只要从经典题(比如数字三角形、爬楼梯)入手,相信我也可以越做越好!

浙公网安备 33010602011771号