问题内容
基本描述
给定一个三角形 triangle ,找出自顶向下的最小路径和:每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。
实例
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
输入:triangle = [[-10]]
输出:-10
算法求解-动态规划
思路
用
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示从三角形顶部走到位置(i,j)的最小路径和。这里的位置
(
i
,
j
)
(i,j)
(i,j)指的是三角形中第
i
i
i行第
j
j
j列(均从0开始编号)的位置。
状态转移方程为
f
[
i
]
[
j
]
=
m
i
n
(
f
[
i
−
1
]
[
j
−
1
]
,
f
[
i
−
1
]
[
j
]
)
+
c
[
i
]
[
j
]
f[i][j]=min(f[i-1][j-1], f[i-1][j]) +c[i][j]
f[i][j]=min(f[i−1][j−1],f[i−1][j])+c[i][j]
其中
c
[
i
]
[
j
]
c[i][j]
c[i][j]表示三角形中位置
(
i
,
j
)
(i,j)
(i,j)对应的元素值。
由于第
i
i
i行有
i
+
1
i+1
i+1个元素,他们对应的j的范围为
[
0
,
i
]
[0,i]
[0,i]。当
j
=
0
j=0
j=0或
j
=
i
j=i
j=i时,上述方程中有一些是没有意义的。
当
j
=
0
j=0
j=0时,
f
[
i
−
1
]
[
j
−
1
]
f[i-1][j-1]
f[i−1][j−1]无意义,此时状态转移方程变为
f
[
i
]
[
0
]
=
f
[
i
−
1
]
[
0
]
+
c
[
i
]
[
0
]
f[i][0]=f[i-1][0] +c[i][0]
f[i][0]=f[i−1][0]+c[i][0]
即当我们在第
i
i
i行的最左侧时,只能从第
i
−
1
i-1
i−1行的最左侧移动过来。
当
j
=
i
j=i
j=i时,
f
[
i
−
1
]
[
j
]
f[i-1][j]
f[i−1][j]无意义,此时状态转移方程变为
f
[
i
]
[
i
]
=
f
[
i
−
1
]
[
i
−
1
]
+
c
[
i
]
[
i
]
f[i][i]=f[i-1][i-1] +c[i][i]
f[i][i]=f[i−1][i−1]+c[i][i]
即当我们在第
i
i
i行的最右侧时,只能从第
i
−
1
i-1
i−1行的最右侧移动过来。
最终的答案即为
f
[
n
−
1
]
[
0
]
f[n-1][0]
f[n−1][0]到
f
[
n
−
1
]
[
n
−
1
]
f[n-1][n-1]
f[n−1][n−1]中的最小值,其中
n
n
n是三角形的行数。
该问题中,边界为
f
[
0
]
[
0
]
=
c
[
0
]
[
0
]
f[0][0]=c[0][0]
f[0][0]=c[0][0]
即在三角形的顶部时,最小路径和就等于对应位置的元素值。这样一来,我么从1开始递增地枚举
i
i
i,并在
[
0
,
i
]
[0,i]
[0,i]的范围递增地枚举
i
i
i,就可以完成所有状态的计算。
代码
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int [][] f = new int[n][n];
f[0][0] = triangle.get(0).get(0);
for (int i = 1; i < n; i++) {
f[i][0] = f[i-1][0] + triangle.get(i).get(0);
for (int j = 1; j < i; j++) {
f[i][j] = triangle.get(i).get(j) + Math.min(f[i-1][j-1], f[i-1][j]);
}
f[i][i] = f[i-1][i-1] + triangle.get(i).get(i);
}
int minTSP = f[n-1][0];
for (int i = 1; i < n; i++) {
if (f[n-1][i] < minTSP) {
minTSP = f[n-1][i];
}
}
return minTSP;
}
复杂度分析
时间复杂度:用到两重for循环,为
O
(
n
2
)
O(n^2)
O(n2),其中
n
n
n是三角形的行数。
空间复杂度:用到
f
[
n
−
1
]
[
n
−
1
]
f[n-1][n-1]
f[n−1][n−1],为
O
(
n
2
)
O(n^2)
O(n2)。
算法求解-动态规划+空间优化
思路
回顾上一节的状态转移方程,可以发现, f [ i ] [ j ] f[i][j] f[i][j]只和 f [ i − 1 ] [ . . . ] f[i-1][...] f[i−1][...]相关,和 f [ i − 2 ] [ . . . ] f[i-2][...] f[i−2][...]及更早的状态无关,因为只需要使用2个一维数组存储即可。
代码
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int [][] f = new int[2][n];
f[0][0] = triangle.get(0).get(0);
f[1][0] = f[0][0]; // 防止仅有一行
for (int i = 1; i < n; i++) {
f[1][0] = f[0][0] + triangle.get(i).get(0);
for (int j = 1; j < i; j++) {
f[1][j] = triangle.get(i).get(j) + Math.min(f[0][j-1], f[0][j]);
}
f[1][i] = f[0][i-1] + triangle.get(i).get(i);
for (int j = 0; j <= i; j++) {
f[0][j] = f[1][j];
}
}
int minTSP = f[1][0];
for (int i = 1; i < n; i++) {
if (f[1][i] < minTSP) {
minTSP = f[1][i];
}
}
return minTSP;
}
复杂度分析
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)。
空间复杂度:
O
(
n
)
O(n)
O(n)。