Description:
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).
Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.
解题思路:
(1)动态规划法
一道动态规划的经典题目。需要自底向上求解。
dp[i][j]表示走到第arr[i][j]时的最优解。
递推公式是: dp[i][j] = Min( dp[i+1][j] , dp[i+1][j+1] ) + arr[i][j],当前这个点的最小值,由它下面那一行临近的2个点的最小值与当前点的值相加得到。
由于是三角形,且历史数据只在计算最小值时应用一次,所以无需建立二维数组,每次更新1维数组值,最后那个值里存的就是最终结果。
已经AC的代码:
import java.util.ArrayList;
import java.util.List;
public class triangle_120 {
public static void main(String[] args) {
// TODO Auto-generated method stub
List> triangle = new ArrayList<>();
List line1 = new ArrayList<>();
line1.add(2);
triangle.add(line1);
List line2 = new ArrayList<>();
line2.add(3);
line2.add(4);
triangle.add(line2);
List line3 = new ArrayList<>();
line3.add(6);
line3.add(5);
line3.add(7);
triangle.add(line3);
List line4 = new ArrayList<>();
line4.add(4);
line4.add(1);
line4.add(8);
line4.add(3);
triangle.add(line4);
System.out.println(minimumTotal(triangle));
}
public static int minimumTotal(List> triangle) {
if(triangle.size()==1)
return triangle.get(0).get(0);
int[] dp = new int[triangle.size()];
//initial by last row
for (int i = 0; i < triangle.get(triangle.size() - 1).size(); i++) {
dp[i] = triangle.get(triangle.size() - 1).get(i);
}
// iterate from last second row
for (int i = triangle.size() - 2; i >= 0; i--) {
for (int j = 0; j < triangle.get(i).size(); j++) {
dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);
}
}
return dp[0];
}
}
(2)递归解法
同样的动态规划,采用深度优先,很容易想到用递归法,但是这种方法在数据量很大时超时了。
不能AC的代码:
import java.util.ArrayList;
import java.util.List;
public class triangle_120 {
public static void main(String[] args) {
// TODO Auto-generated method stub
List> triangle = new ArrayList<>();
List line1 = new ArrayList<>();
line1.add(2);
triangle.add(line1);
List line2 = new ArrayList<>();
line2.add(3);
line2.add(4);
triangle.add(line2);
List line3 = new ArrayList<>();
line3.add(6);
line3.add(5);
line3.add(7);
triangle.add(line3);
List line4 = new ArrayList<>();
line4.add(4);
line4.add(1);
line4.add(8);
line4.add(3);
triangle.add(line4);
System.out.println(minimumTotal(triangle));
}
public static int minimumTotal(List> triangle) {
return findMinPath(triangle, 0, Integer.MAX_VALUE, 0, 0);
}
/**
* 递归求最小路径
* @param triangle
* @param curSum 当前最小值
* @param min 全局最小值
* @param index 每一行中的List元素下标
* @param level 行下标
* @return
*/
public static int findMinPath(List> triangle, int curSum, int min, int index, int level) {
curSum += triangle.get(level).get(index);
if (level == triangle.size() - 1)
return Math.min(min, curSum);
return Math.min(findMinPath(triangle, curSum, min, index, level + 1),
findMinPath(triangle, curSum, min, index + 1, level + 1));
}
}
(3)对上面递归法的优化 - 递归法
上面递归法有一个问题就是,递归函数中无用的参数过多,导致看起来程序很复杂。下面的递归解法,优化了递归函数中的参数。
import java.util.ArrayList;
import java.util.List;
public class triangle_120 {
public static void main(String[] args) {
// TODO Auto-generated method stub
List> triangle = new ArrayList<>();
List line1 = new ArrayList<>();
line1.add(2);
triangle.add(line1);
List line2 = new ArrayList<>();
line2.add(3);
line2.add(4);
triangle.add(line2);
List line3 = new ArrayList<>();
line3.add(6);
line3.add(5);
line3.add(7);
triangle.add(line3);
List line4 = new ArrayList<>();
line4.add(4);
line4.add(1);
line4.add(8);
line4.add(3);
triangle.add(line4);
System.out.println(minimumTotal(triangle));
}
public static int minimumTotal(List> triangle) {
return findMinPath2(triangle, 0, 0);
}
public static int findMinPath2(List> triangle, int row, int col) {
if(row == triangle.size() - 1)
return triangle.get(row).get(col);
return Math.min(findMinPath2(triangle, row+1, col),
findMinPath2(triangle, row+1, col+1)) + triangle.get(row).get(col);
}
}
(4)记忆化搜索法
由于递归解法中有很多的重复计算,我们可以用数组保存已经计算过的结果,这样就不存在重复计算,时间效率会大大提高。
声名一个一维数组memo,数组的大小是这个三角形元素的个数。数组中每个元素代表的是三角形中到这个元素的最小路径和。
已经AC的代码:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class triangle_120 {
public static void main(String[] args) {
// TODO Auto-generated method stub
List> triangle = new ArrayList<>();
List line1 = new ArrayList<>();
line1.add(2);
triangle.add(line1);
List line2 = new ArrayList<>();
line2.add(3);
line2.add(4);
triangle.add(line2);
List line3 = new ArrayList<>();
line3.add(6);
line3.add(5);
line3.add(7);
triangle.add(line3);
List line4 = new ArrayList<>();
line4.add(4);
line4.add(1);
line4.add(8);
line4.add(3);
triangle.add(line4);
System.out.println(minimumTotal(triangle));
}
public static int[] memo = null;
public static int minimumTotal(List> triangle) {
memo = new int[(triangle.size()*(1+triangle.size()))/2];
Arrays.fill(memo, -1);
return findMinPath2(triangle, 0, 0);
}
public static int findMinPath2(List> triangle, int row, int col) {
if(row == triangle.size() - 1) {
return triangle.get(row).get(col);
}
if(memo[(row * (row + 1) / 2) + col] != -1)
return memo[(row * (row + 1) / 2) + col];
else {
int A = findMinPath2(triangle, row+1, col) + triangle.get(row).get(col);
int B = findMinPath2(triangle, row+1, col+1) + triangle.get(row).get(col);
int minValue = Math.min(A, B);
memo[(row * (row + 1) / 2) + col] = minValue;
return minValue;
}
}
}
【1】https://blog.csdn.net/happyaaaaaaaaaaa/article/details/50826642
【2】https://blog.csdn.net/zxzxzx0119/article/details/83155087