给出一个三角形,计算从三角形顶部到底部的最小路径和,每一步都可以移动到下面一行相邻的数字,
例如,给出的三角形如下:
[[20],[30,40],[60,50,70],[40,10,80,30]]最小的从顶部到底部的路径和是20 + 30 + 50 + 10 = 110。
class Solution { public: //从下往上找,不使用额外空间,缺点是修改了输入 int minimumTotal(vector<vector<int> > &***) { for(int i=***.size()-2;i>=0;--i){ for(int j=0;j<***[i].size();++j){ ***[i][j]+=min(***[i+1][j],***[i+1][j+1]); } } return ***[0][0]; } //使用O(n)的额外空间,不修改原输入 int minimumTotal(vector<vector<int> > &***) { vector<int> res(***.back()); for(int i=***.size()-2;i>=0;--i){ for(int j=0;j<***[i].size();++j){ res[j]=***[i][j]+min(res[j],res[j+1]); } } return res[0]; } };
import java.util.*; public class Solution { public int minimumTotal(ArrayList<ArrayList<Integer>> ***) { for (int i = ***.size() - 2; i >= 0; i --) { for (int j = 0; j < ***.get(i + 1).size() - 1; j ++) { int min = Math.min(***.get(i + 1).get(j), ***.get(i + 1).get(j + 1)); ***.get(i).set(j, ***.get(i).get(j) + min); } } return ***.get(0).get(0); } }
// 给定一个三角形,找出从顶到底的最小路径和,每一步可以从上一行移动到下一行相邻的数字 // [ // [2], [2], // [3,4], [3, 4], [2], // [6,5,7], ==> [7, 6, 10] ==> [9, 10] ==> [11] // [4,1,8,3] // ] /**思路: * 自底向上 dp: 不需要额外的空间 * dp[i][j] = min(dp[i+1][j], dp[i+1][j+1]) + ***[i][j] * dp[i][j]: 表示到达 (i, j)最小路径的总和 */ // 修改输入样例: // 4 // 2 // 3 4 // 6 5 7 // 4 1 8 3 #include <iostream> #include <vector> #include <fstream> using namespace std; class Solution { public: int minimumTotal(vector<vector<int> > &***) { int n = ***.size(); for(int i = n - 2; i >= 0; i--) { for(int j = 0; j <= i; j++) { ***[i][j] += min(***[i+1][j], ***[i+1][j+1]); } } return ***[0][0]; } }; int main() { ifstream infile("in.txt", ifstream::in); #define cin infile vector<vector<int> > ***; int n; cin >> n; for(int i = 0; i < n; i++) { vector<int> vi(i+1, 0); for(int j = 0; j < i+1; j++) cin >> vi[j]; ***.push_back(vi); } Solution* s = new Solution(); cout << s->minimumTotal(***) << endl; return 0; }
import java.util.ArrayList; /* 可以发现一个规律:第l个数组的第k个元素的下一个元素的有两种可能,分别是第l+1个数组的第k个元素与第k+1个元素。所以递归取最小值搞定 */ public class Solution { public int minimumTotal(ArrayList<ArrayList<Integer>> ***) { int sum ; sum = getResult(***,0,0); return sum; } public int getResult(ArrayList<ArrayList<Integer>> ***,int l,int k) { int sum = ***.get(l).get(k); if(l<***.size()-1) sum = sum+Math.min(getResult(***,l+1,k),getResult(***,l+1,k+1)); return sum; } }
// 贴一个二叉树的思路。
/**
*
* 整个三角形可以看做一个二叉树,用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);
}
}
class Solution { public: int minimumTotal(vector<vector<int> > &***) { int len = ***.size(); vector<int> dp(len, 0); for(int i = len-1; i >= 0; --i){ for(int j = 0; j <= i; ++j){ if(i == len-1){ dp[j] = ***[i][j]; }else{ dp[j] = ***[i][j] + min(dp[j], dp[j+1]); } } } return dp[0]; } };
import java.util.ArrayList; public class Solution { public int minimumTotal(ArrayList<ArrayList<Integer>> ***) { int size = ***.size(); ArrayList<Integer> arr = ***.get(size - 1); for(int i = size - 2; i >= 0; i--){ for(int j = 0; j <= i; j++){ arr.set(j, Math.min(arr.get(j), arr.get(j + 1)) + ***.get(i).get(j)); } } return arr.get(0); } }
//从后往上
public int minimumTotal(ArrayList<ArrayList<Integer>> ***) {
if(***==null||***.size()==0)
return 0;
int[] dp=new int[***.size()];
ArrayList<Integer> last= ***.get(***.size()-1);
//最后一层复制给dp数组
for(int i=0;i<last.size();i++)
dp[i]=last.get(i);
//最后第二层开始
for(int i=***.size()-2;i>=0;i--){
ArrayList<Integer> curList =***.get(i);
//每个节点跟下一层两个节点进行比较
for(int j=0;j<curList.size();j++)
dp[j]=Math.min(curList.get(j)+dp[j],curList.get(j)+dp[j+1]);
}
return dp[0];
}
/** * 方法:动态规划 * 状态: * 子状态:从(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; } }
class Solution { public: int minimumTotal(vector<vector<int> > &triangle) { if(triangle.empty()){ return 0; } int n = triangle.size(); vector<vector<int>> F(triangle); for(int i = n - 2; i >= 0; --i){ for(int j = 0; j <= i; ++j){ F[i][j] = min(F[i + 1][j], F[i + 1][j + 1]) + F[i][j]; } } return F[0][0]; } };
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]; } }
class Solution { public: int minimumTotal(vector<vector<int> > &***) { int rows=***.size(); int dp[rows][rows]; dp[0][0]=***[0][0]; for(int i=1;i<rows;i++){ dp[i][0]=***[i][0]+dp[i-1][0]; dp[i][i]=***[i][i]+dp[i-1][i-1]; } for(int i=2;i<rows;i++){ for(int j=1;j<i;j++) dp[i][j]=***[i][j]+min(dp[i-1][j-1],dp[i-1][j]); } int res=dp[rows-1][0]; for(int i=1;i<rows;i++) res=min(res,dp[rows-1][i]); return res; } };
class Solution { public: int minimumTotal(vector<vector<int> > &***) { int* pDP = new int[***.size() + 1]; for (int i = 0; i <= ***.size(); ++i) pDP[i] = INT_MAX; pDP[0] = ***[0][0]; for (int i = 1; i < ***.size(); ++i) { for (int j = i; j >= 1; --j) pDP[j] = ***[i][j] + min(pDP[j - 1], pDP[j]); pDP[0] += ***[i][0]; } int nMin = INT_MAX; for (int i = 0; i < ***.size(); ++i) if (nMin > pDP[i]) nMin = pDP[i]; delete[] pDP; return nMin; } };
class Solution { public: int minimumTotal(vector<vector<int> > &***) { if(***.empty())return 0; vector<int>dp(***.back().size()); for(int j=0;j<(int)***.back().size();j++)dp[j]=***.back()[j]; for(int i=(int)***.size()-2;i>=0;i--) for(int j=0;j<(int)***[i].size();j++) dp[j]=***[i][j]+min(dp[j],dp[j+1]); return dp[0]; } };
//以某个节点为终点的最短路径,等于以该节点的各个前驱节点为终点的最短路径中 //最短的值,加上该节点的值 //状态转移方程 :dist[j] = minval(dist[j],dist[j-1]) + ***(i,j); class Solution { public: int minimumTotal(vector<vector<int> > &***) { int n = ***.size(); int dist[n]; //空间复杂度O(n) dist[0] = ***[0][0]; int mindist; for(int i = 2;i<=n;i++){ for(int j=i-1;j>=0;j--){ if(j==0) dist[j] = dist[0] + ***[i-1][j]; else if(j==i-1) dist[j] = dist[j-1] + ***[i-1][j]; else dist[j] = minval(dist[j],dist[j-1]) + ***[i-1][j]; //状态转移方程 } } for(int k=0;k<n;k++){ if(k==0) mindist=dist[0]; else if(mindist > dist[k]) mindist = dist[k]; } return mindist; } int minval(int a, int b){ return a > b ? b : a; } };
class Solution { public: int minimumTotal(vector<vector<int> > &***) { int h = ***.size(); if (h == 1) { return ***[0][0]; } ***[1][0] += ***[0][0]; ***[1][1] += ***[0][0]; for (int i = 2; i < h; i++) { ***[i][0] += ***[i - 1][0]; for (int j = 1; j < ***[i].size()-1; j++) { int temp = ***[i - 1][j] < ***[i - 1][j - 1] ? ***[i - 1][j] : ***[i - 1][j - 1]; ***[i][j]+=temp; } ***[i][i] += ***[i - 1][i-1]; } int min = ***[h - 1][0]; for (int i = 1; i <= h-1; i++) { if (***[h - 1][i] < min) { min = ***[h - 1][i]; } } return min; } };
/** * @Description DFS recursion 简单易懂。 */ public int minimumTotal(ArrayList<ArrayList<Integer>> ***) { return DFS(***, 0, 0); } private int DFS(ArrayList<ArrayList<Integer>> *** , int i , int j) { return i == ***.size()-1?***.get(i).get(j):***.get(i).get(j)+ Math.min(DFS(*** , i+1 ,j), DFS(***, i+1 ,j+1)); }
import java.util.HashMap; import java.util.ArrayList; public class Solution { public int minimumTotal(ArrayList<ArrayList<Integer>> ***) { if (*** == null || ***.get(0) == null) { return 0; } //HashMap<String, Integer> record = new HashMap<String, Integer>(); //return dfs2(***, 0, 0, record); return dp(***); } /** * 递归: * 自顶至下递归,用当前结点的值加上左右子结点(子路径)中较小的值 * * 优点:简单易懂 * 缺点:,记行数为n,递归深度约为2^n,存在大量的重复计算 **/ private static int dfs(ArrayList<ArrayList<Integer>> tri, int row, int col) { if (row >= tri.size()) { return 0; } int val = tri.get(row).get(col); return val + Math.min(dfs(tri, row + 1, col), dfs(tri, row + 1, col + 1)); } /** * 改进版递归: * 利用map记录计算过的结点,减少重复工作 * * 但这个方法不符合题意中空间复杂度O(n)的限制。 **/ private static int dfs2(ArrayList<ArrayList<Integer>> tri, int row, int col, HashMap<String, Integer> record) { if (row >= tri.size()) { return 0; } String key = new StringBuilder().append(row).append(" ").append(col).toString(); if (record.containsKey(key)) { return record.get(key); } int val = tri.get(row).get(col); int min = val + Math.min(dfs2(tri, row + 1, col, record), dfs2(tri, row + 1, col + 1, record)); record.put(key, min); return min; } /** * 动态规划: * 自底至上计算(因为这样可以减少重复的计算工作) * 在每一行中,计算每个结点加上左右子结点(子路径)中较小的值,并放入当前结点, * 然后,循环计算,直到顶点,最终的tri.get(0).get(0)即为所求结果。 **/ private static int dp(ArrayList<ArrayList<Integer>> tri) { for (int i = tri.size() - 2; i >= 0; i--) { for (int j = 0; j < tri.get(i).size(); j++) { tri.get(i).set(j, tri.get(i).get(j) + Math.min(tri.get(i + 1).get(j), tri.get(i + 1).get(j + 1))); } } return tri.get(0).get(0); } }