1. 问题描述:
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
说明:
如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/triangle
2. 思路分析:
① 这道题目是经典使用动态规划解决的题目,可以使用一个二维数组来记录当前得到的路径的最小值,从三角形的最上端开始,对于当前位置的下面两个相邻的数字进行相加,比如一开始的时候dp数组第二行第一列的位置等于2 + 3 = 5,第二行第二列的位置等于2 + 4 = 6,dp[i][j]表示的是到达i行j列位置的最长路径和,这个其实还是比较好理解的,这里要注意一个细节就是对于每一行第一列的位置往下进行计算的时候直接累加下面的两个数字即可,比如对于第一行第一列的数字2,对应的dp二维数组的第二行第一列与二列的位置的值为5,6,列数超过了第一类的时候需要进行比较得到当前位置的最小值,因为到达当前的位置存在着两条路径,说起来很绕,比如对于第三行中的第二个位置对应数字5之前在第一次的时候计算过这个位置的值,为2 +3 + 5 = 10,同时存在着另外一条路径2 + 4 + 5所以当不是第一列的时候我们需要对之前得到的值与现在到达这个位置的值进行比较这样的话得到的值才是最小的,结合代码很容易理解
② 最后对得到的二维数组的最一行进行遍历得到最小路径值。可以在纸上画出具体的例子就比较好理解了,并且可以很好处理其中的细节
3. 代码如下:
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
//对于一个数字的处理
if (triangle.size() == 1) return triangle.get(0).get(0);
int min = Integer.MAX_VALUE;
int rec[][] = new int[triangle.size()][];
for (int i = 0; i < triangle.size(); ++i){
rec[i] = new int[i + 1];
}
rec[0][0] = triangle.get(0).get(0);
for (int i = 0; i < triangle.size() - 1; ++i){
List<Integer> curLine = triangle.get(i);
List<Integer> nextLine = triangle.get(i + 1);
for (int j = 0; j < triangle.get(i).size(); ++j){
int first = nextLine.get(j);
int second = nextLine.get(j + 1);
int cur = rec[i][j];
/*当为第一个的时候应该是直接相加即可其余的情况进行最小值的判断因为到达这个位置存在着两条路径需要对这两条路径进行比较*/
if (j == 0){
//j == 0的情况
rec[i + 1][j] = first + cur;
rec[i + 1][j + 1] = second + cur;
}else {
//j > 1的情况需要对之前得到的值进行比较
rec[i + 1][j] = Math.min(rec[i + 1][j], cur + first);
//对于第二个位置直接相加因为在下一次的时候这个位置会转为第一个位置会进行最小值的比较在纸上画一下图是很好理解的
rec[i + 1][j + 1] = second + cur;
}
}
}
for (int i = 0; i < triangle.size(); ++i){
min = Math.min(min, rec[triangle.size() - 1][i]);
}
return min;
}
}
添加了输入测试代码,在控制台输入比较好验证代码是否正确:
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
/*题目中的方法是以List形式来给出每一行的数字的*/
List<List<Integer>> triangle = new ArrayList<>();
int n = sc.nextInt();
for (int i = 0; i < n; ++i){
List<Integer> cur = new ArrayList<>();
for (int j = 0; j <= i; ++j){
cur.add(sc.nextInt());
}
triangle.add(cur);
}
System.out.println(minimumTotal(triangle));
}
public static int minimumTotal(List<List<Integer>> triangle) {
if (triangle.size() == 1) return triangle.get(0).get(0);
int min = Integer.MAX_VALUE;
int rec[][] = new int[triangle.size()][];
for (int i = 0; i < triangle.size(); ++i){
rec[i] = new int[i + 1];
}
rec[0][0] = triangle.get(0).get(0);
for (int i = 0; i < triangle.size() - 1; ++i){
List<Integer> curLine = triangle.get(i);
List<Integer> nextLine = triangle.get(i + 1);
for (int j = 0; j < triangle.get(i).size(); ++j){
int first = nextLine.get(j);
int second = nextLine.get(j + 1);
int cur = rec[i][j];
/*当为第一个的时候应该是直接相加即可其余的情况进行最小值的判断*/
if (j == 0){
//j == 0的情况
rec[i + 1][j] = first + cur;
rec[i + 1][j + 1] = second + cur;
}else {
//j == 1的情况
rec[i + 1][j] = Math.min(rec[i + 1][j], cur + first);
rec[i + 1][j + 1] = second + cur;
}
}
}
for (int i = 0; i < triangle.size(); ++i){
min = Math.min(min, rec[triangle.size() - 1][i]);
}
return min;
}
}