给出一个三角形,计算从三角形顶部到底部的最小路径和,每一步都可以移动到下面一行相邻的数字,
例如,给出的三角形如下:
[[20],[30,40],[60,50,70],[40,10,80,30]]最小的从顶部到底部的路径和是20 + 30 + 50 + 10 = 110。
/** * 方法:动态规划 * 状态: * 子状态:从(0,0)到(1,0),(1,1),(2,0),...(n,n)的最短路径和 * F(i,j): 从(0,0)到(i,j)的最短路径和 * 状态递推: * F(i,j) = min( F(i-1, j-1), F(i-1, j)) + triangle[i][j] * 初始值: * F(0,0) = triangle[0][0] * 返回结果: * min(F(n-1, i)) * 最后一行最小的元素 */ public class Solution { public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) { if (triangle == null) return 0; ArrayList<ArrayList<Integer>> result = new ArrayList<>(); int row = triangle.size(); for (int i = 0; i < row; i++) { result.add(new ArrayList<>()); } // F[0][0]初始化 result.get(0).add(triangle.get(0).get(0)); for (int i = 1; i < row; i++) { //上一行的最小值 int curSum = 0; for (int j = 0; j < triangle.get(i).size(); j++) { // 处理左边界和右边界 if (j == 0) { curSum = result.get(i - 1).get(0); } else if (j == i) { curSum = result.get(i - 1).get(j - 1); } else { curSum = Math.min(result.get(i - 1).get(j - 1), result.get(i - 1).get(j)); } // F(i,j) = min( F(i-1, j-1), F(i-1, j)) + triangle[i][j] result.get(i).add(curSum + triangle.get(i).get(j)); } } int size = triangle.size(); // min(F(n-1, i)) int allMin = result.get(size - 1).get(0); //在最后一行找最小的值 for (int i = 1; i < result.get(size - 1).size(); i++) { allMin = Math.min(result.get(size - 1).get(i), allMin); } return allMin; } }
import java.util.ArrayList; public class Solution { public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) { if(triangle.size()==0) return 0; if(triangle.size()==1) return triangle.get(0).get(0); int[] dp=new int[triangle.size()+1]; for(int i=triangle.size()-1;i>=0;i--){ ArrayList<Integer> cur=triangle.get(i); for(int j=0;j<cur.size();j++){ dp[j]=Math.min(dp[j],dp[j+1])+cur.get(j); } } return dp[0]; } }
import java.util.*; public class Solution { public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) { if(triangle==null||triangle.size()<=0) return 0 ; ArrayList<ArrayList<Integer>> dp = new ArrayList<ArrayList<Integer>>(); dp.add(triangle.get(0)); for(int i = 1; i <triangle.size(); i++){ ArrayList<Integer> tmp = new ArrayList<Integer>(); for(int j = 0;j < triangle.get(i).size();j++){ int d = Math.min(dp.get(i-1).get(Math.max(j-1,0))+triangle.get(i).get(j),dp.get(i-1).get(Math.min(j,dp.get(i-1).size()-1))+triangle.get(i).get(j)); tmp.add(d); } dp.add(tmp); } return Collections.min(dp.get(triangle.size()-1)); } }
import java.util.*; class Solution { /** * DP,复用一个长度triangle.size()大小的数组记录 * 上一层元素的构成的小三角的最小路径和,状态转移方程 * f(i,j) = min(f(i+1, j)+triangle[i][j], f(i+1, j+1)+triangle[i][j]) */ public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) { if(triangle.size()<1) return 0; int[] min = new int[triangle.size()]; for(int i=0; i<triangle.size(); i++){ min[i] = triangle.get(triangle.size()-1).get(i); } for(int i=triangle.size()-2; i>=0; i--){ for(int j=0; j<=i; j++){ min[j] = Math.min(min[j]+triangle.get(i).get(j), min[j+1]+triangle.get(i).get(j)); } } return min[0]; } }
import java.util.ArrayList; public class Solution { public int minimumTotal(ArrayList<ArrayList<Integer>> ***) { int size=***.size(); int[][]min=new int[size][si***[0][0]=***.get(0).get(0); int result=0; for(int i=1;i<size;i++) {for(int j=0;j<i+1;j++) if(j>0&&i!=j) {min[i][j]=Math.min(min[i-1][j-1]+***.get(i).get(j),min[i-1][j]+***.get(i).get(j));} else{ if(j==0) {min[i][j]=min[i-1][j]+***.get(i).get(j);} else min[i][j]=min[i-1][j-1]+***.get(i).get(j);} } result=min[size-1][0]; for(int m=0;m<size;m++) {if(min[size-1][m]<result) result=min[size-1][m];} return result; } }
import java.util.*; public class Solution { public int minimumTotal(ArrayList<ArrayList<Integer>> ***) { int[][] result = new int[***.size()][]; result[0] = new int[1]; result[0][0] = ***.get(0).get(0); for (int i = 1; i < result.length; i++) { ArrayList<Integer> temp = ***.get(i); result[i] = new int[temp.size()]; result[i][0] = result[i - 1][0] + temp.get(0); for (int j = 1; j < temp.size() - 1; j++) { result[i][j] = Math.min(result[i - 1][j],result[i - 1][j - 1]) + temp.get(j); } result[i][temp.size() - 1] = result[i - 1][result[i - 1].length - 1] + temp.get(temp.size() - 1); } int theResult = Integer.MAX_VALUE; for (int current : result[***.size() - 1]) { theResult = Math.min(theResult,current); } return theResult; } }
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class Bonus {
public int minimumTotal(ArrayList<ArrayList<Integer>> ***) {
if (***.size() == 0)
return 0;
HashMap<Integer, HashMap<Integer, Integer>> map = new HashMap<>();
HashMap<Integer, Integer> rowZero = new HashMap<>();
rowZero.put(0, ***.get(0).get(0));
map.put(0, rowZero);
for (int i = 1; i < ***.size(); i++) {
HashMap<Integer, Integer> thisRow = new HashMap<>();
ArrayList<Integer> list = ***.get(i);
HashMap<Integer, Integer> m = map.get(i - 1);
thisRow.put(0, m.get(0) + list.get(0));
thisRow.put(i, m.get(i - 1) + list.get(i));
map.put(i, thisRow);
}
for (int i = 1; i < ***.size(); i++) {
ArrayList<Integer> list = ***.get(i);
for (int j = 1; j < list.size() - 1; j++) {
HashMap<Integer, Integer> thisRow = map.get(i);
HashMap<Integer, Integer> upperRow = map.get(i - 1);
thisRow.put(j, Math.min(list.get(j) + upperRow.get(j), list.get(j) + upperRow.get(j - 1)));
}
}
int min = Integer.MAX_VALUE;
for (Map.Entry<Integer, Integer> entry : map.get(***.size() - 1).entrySet()) {
min = Math.min(min, entry.getValue());
}
return min;
}
}
import java.util.ArrayList; public class Solution { public int minimumTotal(ArrayList<ArrayList<Integer>> ***) { for(int i = ***.size()-1; i > 0; i--){ ArrayList<Integer> list0 = ***.get(i); ArrayList<Integer> list1 = ***.get(i-1); for(int j = 0; j < list1.size(); j++){ int min = Math.min(list0.get(j), list0.get(j+1)); list1.set(j, list1.get(j)+min); } } return ***.get(0).get(0); } }
}
// 贴一个二叉树的思路。
/**
*
* 整个三角形可以看做一个二叉树,用f表示二叉树深度,从1到n。
* 二维ArrayList<ArrayList<Integer>> 可以看做是一个一维数组,其中index表示数组的游标,从0到len-1。
* 左孩子就是当前节点加上这层孩子的数目f,也就是index+f
* len是一维数组,也可以用来约束一维数组的index是否越界,换句话,就是可以判断叶子节点。
*
*/
public class Solution {
int len;
public int minimumTotal(ArrayList<ArrayList<Integer>> ***) {
len = ***.size() * (***.size() + 1) / 2;
return minSum(***, 0, 1);
}
/**
* 用来表示某棵树的路径最小的节点之和,无非两种情况,选择左树或者右树
*/
private int minSum(ArrayList<ArrayList<Integer>> ***, int index, int f) {
if (index >= len) return 0;
int leftVal = minSum(***, index + f, f + 1); //左树的最小路径节点和
int rightVal = minSum(***, index + f + 1, f + 1); //右树的最小路径节点和
List<Integer> list = ***.get(f - 1); //获取层 数组
int val = list.get(index - (f - 1) * f / 2); //换算出在当前数组的位置,根据1...n的连续和公式,前f-1层一共有 (f-1)*f/2个节点
return Math.min(leftVal + val, rightVal + val);
}
}
主要的思路是从上往下,将到每个点的最小路径保存下来,从上到下遍历一遍,在当前点存储最小路径的值,不需要额外的空间,时间复杂度为o(n),最后在最后一层将最小的点找出来。
public int minimumTotal(ArrayList<ArrayList<Integer>> ***) {
if(***.size() == 0) return 0;
for(int i = 1; i < ***.size(); i++){
ArrayList<Integer> prelist = ***.get(i - 1);
ArrayList<Integer> list = ***.get(i);
//每行第一个点和最后一个点只能从上一行的第一个点及最后一个点走下来
list.set(0,list.get(0) + prelist.get(0));
list.set(list.size() - 1,list.get(list.size() - 1) + prelist.get(prelist.size() - 1));
for(int j = 1; j < list.size() - 1; j++){
//每行其他点判断是从左边的点走下来还是从右边的点走下来
list.set(j,Math.min(list.get(j) + prelist.get(j - 1),list.get(j) + prelist.get(j)));
}
}
ArrayList<Integer> list = ***.get(***.size() - 1);
int min = Integer.MAX_VALUE;
for(int num : list){
min = min < num ? min : num;
}
return min;
}
import java.util.*; public class Solution { public static int minimumTotal(ArrayList<ArrayList<Integer>> ***) { if (*** == null || ***.size() == 0) return 0; List<Integer> midList = new ArrayList<Integer>(***.get(***.size() - 1)); for (int i = ***.size() - 2; i >= 0; --i) { for (int j = 0; j < ***.get(i).size(); ++j) { midList.set(j, Math.min(midList.get(j), midList.get(j + 1)) + ***.get(i).get(j)); } } return midList.get(0); } }
import java.util.*; public class Solution { public int minimumTotal(ArrayList<ArrayList<Integer>> ***) { if(***==null||***.get(0)==null||***.size()<1||***.get(0).size()<1) return 0; ArrayList<Integer> last=new ArrayList<Integer>(); last.add(0); ArrayList<Integer> cur; int res=Integer.MAX_VALUE; for(ArrayList<Integer> list:***){ cur=new ArrayList<Integer>(); for(int i=0;i<list.size();i++){ int val=Math.min((i-1)>=0?last.get(i-1):Integer.MAX_VALUE,i<last.size()?last.get(i):Integer.MAX_VALUE)+list.get(i); cur.add(val); } last=cur; } for(int num:last){ res=Math.min(res,num); } return res; } }
int len = ***.size();
int[] minArray = new int[len];
ArrayList<Integer> temp = ***.get(len-1);
for(int i=0;i<len;i++){
minArray[i] = temp.get(i);
}
for(int i=1;i<len;i++){
temp = ***.get(len-i-1);
for(int j=0;j<=len-i-1;j++){
minArray[j] = Math.min(minArray[j], minArray[j+1])+temp.get(j);
}}return minArray[0];
public class Solution { public int minimumTotal(List<List<Integer>> ***) { for(int i = ***.size() - 2; i >= 0; i--) for(int j = 0; j <= i; j++) ***.get(i).set(j, ***.get(i).get(j) + Math.min(***.get(i + 1).get(j), ***.get(i + 1).get(j + 1))); return ***.get(0).get(0); } }