Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).
给出一个三角形,从顶端走向底端,经过路径的最短数字和是多少,每次只能走向相邻的数字。
原题是一个金字塔形的三角形,写成数组形式就变成了上述形状。
比如2可以走到3,4,3可以走向6,5
也就是说上一行j列到下一行可以走到j或者j+1
反之,j列的上一行可以是j或者j-1
思路:
每一行都求出上一行的数加下来的和,加的时候第j列的数选择上一行j和j-1中较小的相加
到最后一行求完后选择最小的那个值
(1) 二维DP
public int minimumTotal(List<List<Integer>> triangle) {
if(triangle == null || triangle.size() == 0) {
return 0;
}
int n = triangle.size();
//每行的元素数=行数,所以n行n列,补一行
int[][] dp = new int[n+1][n+1];
//后面用到取dp的min,所以先填上最大值
for(int i = 0; i <= n; i++) {
Arrays.fill(dp[i], Integer.MAX_VALUE);
}
for(int i = 1; i <= n; i++) {
//第i行元素只到i
for(int j = 1; j <= i; j++) {
dp[i][j] = triangle.get(i-1).get(j-1);
//第一行不需要和上一行相加,直接填入元素,不需要后续处理
if(i == 1 && j == 1) {
continue;
}
//边界条件
if(j == 1) {
dp[i][j] += dp[i-1][j];
} else if(j == i) {
dp[i][j] += dp[i-1][j-1];
} else {
dp[i][j] += Math.min(dp[i-1][j], dp[i-1][j-1]);
}
}
}
//array中的最大元素,java8
return Arrays.stream(dp[n]).min().getAsInt();
}
(2) 一维DP
因为每次只需要知道上一行的数,还需要保存当前行的数,因此只需要2行的dp数组
每次用dp[1][j]来求当前行的和,然后和dp的0行对换,作为下一次求dp[1]时的参考
c++中有简单的行对换的swap函数,还有求一行最小值的min_element函数
min_element返回iterator,所以用解引用操作符得到数值
int minimumTotal(vector<vector<int>>& triangle) {
int n = triangle.size();
vector<vector<int>> dp(2, vector<int>(n, INT_MAX));
for (int i = 0; i < n; i++) {
for (int j = 0; j <= i; j++) {
dp[1][j] = triangle[i][j];
if (i == 0 && j == 0) {
continue;
}
if (j == 0) {
dp[1][j] += dp[0][j];
} else if(j == i) {
dp[1][j] += dp[0][j - 1];
} else {
dp[1][j] += min(dp[0][j - 1], dp[0][j]);
}
}
std::swap(dp[0], dp[1]);
}
return *std::min_element(dp[0].cbegin(), dp[0].cend());
}
Java版
public int minimumTotal(List<List<Integer>> triangle) {
if(triangle == null || triangle.size() == 0) {
return 0;
}
int n = triangle.size();
//每行的元素数=行数,所以n行n列,补一行
int[][] dp = new int[2][n];
//后面用到取dp的min,所以先填上最大值
for(int i = 0; i <= 1; i++) {
Arrays.fill(dp[i], Integer.MAX_VALUE);
}
for(int i = 0; i < n; i++) {
//第i行元素只到i
for(int j = 0; j <= i; j++) {
dp[1][j] = triangle.get(i).get(j);
//第一行不需要和上一行相加,直接填入元素,不需要后续处理
if(i == 0 && j == 0) {
continue;
}
//边界条件
if(j == 0) {
dp[1][j] += dp[0][j];
} else if(j == i) {
dp[1][j] += dp[0][j-1];
} else {
dp[1][j] += Math.min(dp[0][j], dp[0][j-1]);
}
}
swapArr(dp[0], dp[1]);
}
//array中的最大元素,java8
//每次dp[1]中运算完之后要翻转到dp[0],因此最后看的是dp[0]
return Arrays.stream(dp[0]).min().getAsInt();
}
public void swapArr(int[] arr1, int[] arr2) {
//假设同一长度
for(int i = 0; i < arr1.length; i++) {
int tmp = arr1[i];
arr1[i] = arr2[i];
arr2[i] = tmp;
}
}