题目描述
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。
示例
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
层序遍历
用一个容器保存到达一层所有的可能结果,对于如示例中的5,可由3,4到达,则保存其中一个较小的值:
[2],
[5,6],
[11,10,13],
[15,11,18,16]
计算结果如上图所示。一层一层计算,然后在最后结果中取最小值即可。
public static int minimumTotal(List<List<Integer>> triangle) {
//层序遍历,计算最小路径
//int maxLength = triangle.get(triangle.size() - 1).size();//底层长度
Deque<Integer> deque = new LinkedList<>();
deque.addLast(triangle.get(0).get(0));
for (int i = 1; i < triangle.size(); i++) {
List<Integer> tmpList = triangle.get(i);
int queSize = deque.size();
for (int j = 0; j < queSize; j++) {
int tmp = deque.pollFirst();
deque.addLast(tmp + tmpList.get(j));
deque.addLast(tmp + tmpList.get(j + 1));
}
merge(deque);
}
int min = deque.pollLast();
while (!deque.isEmpty()) {
min = Math.min(min, deque.pollLast());
}
return min;
}
private static void merge(Deque<Integer> deque) {
//中间元素两两合并,取较小者
if (deque.size() <= 3)
return;
int first = deque.pollFirst();
int last = deque.pollLast();
int size = deque.size();
for (int i = 0; i < size / 2; i++) {
deque.addLast(Math.min(deque.pollFirst(), deque.pollFirst()));
}
deque.addFirst(first);
deque.addLast(last);
}
动态规划
在写以上代码的时候发现,到达坐标(i,j)的最小路径之和 与到达坐标(i-1,j-1)以及坐标(i-1,j)有关,取其中一个较小值然后加上当前坐标的值就得到了到达当前坐标的最小路径。对与边界j=0则只和(i-1,j)有关,对于j=i(意味着这个坐标处与第i层的最右边)则只与(i-1,j-1)有关。
所以我们可以采用动态规划来解决此问题。dp[i][j]保存到达坐标(i,j)的最小路径和。动态转移方程也在之前的分析过程给出了。if(j==0)dp[i][j]=dp[i-1][j]+triangle[i][j]
.
if(j==i)dp[i][j]=dp[i-1][i-1]+triangle[i][i];
对于其它情况则有
dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+triangel[i][j]
public static int minimumTotalDP(List<List<Integer>> triangle) {
//dp[i][j]表示顶点到达坐标(i,j)的最小路径长度
//结果在dp[n-1][]这一维数组取最小者
int n = triangle.size();
int m = triangle.get(n - 1).size();
int[][] dp = new int[n][m];
//边界条件,j=0,i=j;
dp[0][0] = triangle.get(0).get(0);
for (int i = 1; i < n; i++) {
dp[i][0] = dp[i - 1][0] + triangle.get(i).get(0);
for (int j = 1; j < i; j++) {
dp[i][j] = Math.min(dp[i - 1][j - 1], dp[i - 1][j]) + triangle.get(i).get(j);
}
dp[i][i] = dp[i - 1][i - 1] + triangle.get(i).get(i);
}
int ans = dp[n - 1][0];
for (int i = 1; i < m; i++) {
ans = Math.min(ans, dp[n - 1][i]);
}
return ans;
}
动态规划+滚动数组
从层序遍历和之前动态规划的代码都可以看出,到达当前层的最小路径和只与到达前一层所需要的最小路径和有关。所以我们只需要用一个一维数组保存到达上一层的最小路径和即可。
public static int minimumTotalDP2(List<List<Integer>> triangle) {
//逆序枚举滚动数组,dp思路不变
int n=triangle.size();
int [] dp=new int[n];
dp[0]=triangle.get(0).get(0);
for(int i=1;i<n;i++){
dp[i]=dp[i-1]+triangle.get(i).get(i);
for(int j=i-1;j>0;j--){
dp[j]=Math.min(dp[j-1],dp[j])+triangle.get(i).get(j);
}
dp[0]=dp[0]+triangle.get(i).get(0);
}
int min=dp[0];
for(int i=1;i<n;i++)
min=Math.min(min,dp[i]);
return min;
}
上面代码与之前动态规划比较起来,主要的变化是用一个一维数组储存路径和。而为了实现这个,我们需要在遍历每层的元素时采用逆序遍历的方式。因为在只有在逆序遍历时,dp[j-1]保存的才是上一层的结果。