首页 > 试题广场 >

三角形

[编程题]三角形
  • 热度指数:30440 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
给出一个三角形,计算从三角形顶部到底部的最小路径和,每一步都可以移动到下面一行相邻的数字,
例如,给出的三角形如下:
[[20],[30,40],[60,50,70],[40,10,80,30]]
最小的从顶部到底部的路径和是20 + 30 + 50 + 10 = 110。
注意:

如果你能只用O(N)的额外的空间来完成这项工作的话,就可以得到附加分,其中N是三角形中的行总数。


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];
    }
};

编辑于 2017-09-05 17:41:03 回复(15)
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);
	}
}
编辑于 2017-03-19 21:48:21 回复(3)
class Solution {
public:
    int minimumTotal(vector<vector<int> > &***) {
        int i,j;
        for(i=***.size()-2;i>=0;--i)
        {
            for(j=0;j<i+1;++j)
            {
                ***[i][j]=***[i][j]+((***[i+1][j]>***[i+1][j+1])?***[i+1][j+1]:***[i+1][j]);
            }
        }
        return ***[0][0];
    }
};

发表于 2016-08-25 16:23:11 回复(14)
// 给定一个三角形,找出从顶到底的最小路径和,每一步可以从上一行移动到下一行相邻的数字
//    [                    
//         [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;
}
编辑于 2017-12-07 20:13:53 回复(3)
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;
    }
}

发表于 2017-09-01 15:53:42 回复(1)
// 贴一个二叉树的思路。

/**
 *
 * 整个三角形可以看做一个二叉树,用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);
    }
}

编辑于 2017-08-31 17:27:08 回复(2)
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];
    }
};

编辑于 2018-03-31 16:55:52 回复(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);
    }
}

发表于 2019-10-21 21:47:36 回复(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];
    }
编辑于 2017-12-05 16:25:23 回复(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;
    }
}

编辑于 2020-08-01 00:26:27 回复(0)
动态规划(反向思维)
状态:
子状态:从(n,n),(n,n-1),...(1,0),(1,1),(0,0)到最后一行的最短路径和
F(i,j): 从(i,j)到最后一行的最短路径和
状态递推:
F(i,j) = min( F(i+1, j), F(i+1, j+1)) + triangle[i][j]
初始值:
F(n-1,0) = triangle[n-1][0], F(n-1,1) = triangle[n-1][1],..., F(n-1,n-1) = triangle[n-1][n-1]
返回结果:
F(0, 0)
这种逆向思维不需要考虑边界,也不需要最后寻找最小值,直接返回F(0,0)即可

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];
    }
};


编辑于 2020-05-10 13:02:43 回复(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];
    }
}

发表于 2020-01-08 19:12:01 回复(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;
    }
};

发表于 2019-10-21 19:14:50 回复(0)
经典动态规划。。。
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;
    }
};

发表于 2019-08-26 17:36:06 回复(0)
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];
    }
};

发表于 2019-08-20 17:46:15 回复(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;
    }
};


发表于 2019-06-30 17:34:47 回复(0)
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;
    }
};

发表于 2019-04-15 16:52:04 回复(0)
/**
 * @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));
}
发表于 2018-10-29 13:51:27 回复(0)
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);
    }
    
}

发表于 2018-10-20 17:05:55 回复(0)
class Solution {
public:
    int MinLast(int *mMinStep, int mi, int mj){
        if(mj == 0)
            return mMinStep[0];
        else if(mj == mi)
            return mMinStep[mi - 1];
        else 
            return mMinStep[mj] <= mMinStep[mj - 1]? mMinStep[mj]:mMinStep[mj - 1];
    }
    int minimumTotal(vector<vector<int> > &***) {
        size_t Num = ***.size();
        int MinSum = 0;
        int *MinStep;
        MinStep = new int[Num];
        for(int i = 0; i != Num; i++){
            MinStep[i] = 0;
        }
        for(int i = 0; i != Num; i++){
            for(int j = i; j>=0; j--){
                MinStep[j] = ***[i][j] + MinLast(MinStep, i, j);
            }
        }
        MinSum = MinStep[0];
        for(int i = 0; i != Num; i++){
            if(MinStep[i] < MinSum)
                MinSum = MinStep[i];
        }
        return MinSum;
    }
};
发表于 2018-07-13 01:59:45 回复(0)