120三角形最小路径和

题目描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
给定一个三角形,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。

相邻的结点
在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。

 

例如,给定三角形:

[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。

 

说明:

如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,

分析

若定义 f(i,j) 为 (i, j)点到底边的最小路径和,则递归求解式为:

由此,将任一点到底边的最小路径和,转化为了与该点相邻两点到底边的最小路径和中的较小值,再加上该点本身的值。

得出(解一) 递归解法。

解一(递归)

1
2
3
4
5
6
7
public int minimumTotal(List<List<Integer>> tri){
return dfs(tri,0,0);
}
private int dfs(List<List<Integer>> tri, int i, int j){
if (i==tri.size()) return 0;
return Math.min(dfs(tri,i+1,j), dfs(tri,i+1, j+1)) + tri.get(i).get(j);
}

缺点:暴力搜索会有大量重复计算,引出解法二,结合记忆化数组进行优化。

解法二:(递归+记忆化)

定义一个二位数组用来记忆化

1
2
3
4
5
6
7
8
9
10
11
Integer[][] memo;

public int minimumTotal(List<List<Integer>> tri){
memo = new Integer[tri.size()][tri.size()];
return dfs(tri,0,0);
}
private int dfs(List<List<Integer>> tri, int i, int j){
if(i==tri.size()) return 0;
if(memo[i][j]!=null) return memo[i][j];
return memo[i][j] = Math.min(dfs(tri, i+1, j), dfs(tri, i+1, j+1)) + tri.get(i).get(j);
}

时间复杂度:$O(N^2)$,N为三角形的行数。
空间复杂度:$O(N^2)$,N为三角形的行数。

动态规划

定义二维 dp 数组,将解法二中「自顶向下的递归」改为「自底向上的递推」。

状态表示:$dp[i][j]$ 表示从点 $(i,j)$ 到底边的最小路径和

状态计算:$dp[i][j] = min(dp[i+1][j] , dp[i+1][j+1]]) + tri[i][j]$

1
2
3
4
5
6
7
8
9
10
11
12
public int minmumTotal(List<List<Integer>> tri){
int n = tri.size();
// dp[i][j] 表示从点(i,j)到底边的最小路径和
int[][] dp = new int[n+1][n+1];
// 从三角形的最后一行开始递推
for(int i=n-1;i>=0;i++){
for(int j=0;j<=i;j++){
dp[i][j]=Math.min(dp[i+1][j], dp[i+1][j+1]) + tri.get(i).get(j);
}
}
return dp[0][0];
}

时间复杂度:$O(N^2)$,N为三角形的行数。
空间复杂度:$O(N^2)$,N为三角形的行数。

空间优化

上一个解定义了一个N行N列的 dp数组

但实际递推中发现,计算 $dp[i][j]$ 时,只用到了下一行的 $dp[i+1][j] 和 dp[i+1][j+1]$

因此dp 数组不需要定义N行,只需定义1行

把 $i$ 所在维度去掉,将就可以将 $O(N^2)$ 的空间复杂度优化成$ O(N)$

1
2
3
4
5
6
7
8
9
10
public int minimumTotal(List<List<Integer>> tri) {
int n = tri.size();
int[] dp = new int[n+1];
for(int i=n-1; j>=0; i--){
for(int j=0;j<=i; j++){
dp[j] = Math.min(dp[j], dp[j+1]) + tri.get(i).get(j);
}
}
return dp[0];
}