lintcode:109 · 数字三角形

题目来源

题目描述

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    /**
     * @param triangle: a list of lists of integers
     * @return: An integer, minimum path sum
     */
    int minimumTotal(vector<vector<int>> &triangle) {
        // write your code here
    }
};

题目解析

分析:

  • 首先明确“每一步可以移动到下面一行的相邻数字上。”是什么意思,即是说当前行的某列 j,可以选择移动到下一行的 j 或者 j+1
  • “找到从顶部到底部的最小路径和”,可以将所有顶部到底部的路径和”求出来然后遍历找到

坐标型动态规划

(1)确定状态

  • 最后一步:
    • 无论用何种方式到达底部,总有最后一步。
    • 设:m = triangle.size(),n = triangle[m - 1].size(),那么最后一步坐标可能是: [m - 1, 0][m - 1, 1][m - 1, 2][m - 1, n-1]
    • 因为“每一步可以移动到下面一行的相邻数字上“,所以:
      • 要走到[m - 1, 0],前一步只可能是[m - 2, 0]
      • 要走到[m - 1, 1],前一步只可能是[m - 2, 0][m - 2, 1]
      • 要走到[m - 1, 2],前一步只可能是[m - 2, 1][m - 2, 2]
      • 要走到[m - 1, n-3],前一步只可能是[m - 2, n -4][m - 2, n - 3]
      • 要走到[m - 1, n-2],前一步只可能是[m - 2, n -3][m - 2, n - 2]
      • 要走到[m - 1, n-1],前一步只可能是[m - 2, n - 2]
    • 最优策略的从顶部到底部的最小路径和,那么:
      • 先使得底部的每一个路径最小,即:
        • [0, 0]走到[m - 1, 0]最小,必须[0, 0][m - 2, 0]的路径最小
        • [0, 0]走到[m - 1, 1]最小,必须[0, 0][m - 2, 0]或者[m - 2, 1]的路径最小的那一个
      • 遍历[m - 1, 0] ~ [m - 1, n-1],找到最小路径和
  • 子问题:
    • 原问题:要求[0,0][i,j]的最小路径和
    • 子问题:先求[0,0][i-1,j-1]的最小路径和以及[0,0][i-1,j]的最小路径和
  • 状态:
    • dp[i][j]:表示从[0,0][i,j]的最小路径和

(2)状态转移方程:

  • 由于每一步只能移动到下一行「相邻的节点」上,因此要想走到位置 [i, j],上一步就只能在位置 [i - 1, j - 1] 或者位置[i - 1, j]。我们在这两个位置中选择一个路径和较小的来进行转移,状态转移方程为:
    d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j − 1 ] , d p [ i − 1 ] [ j ] ) + A [ i ] [ j ] dp[i][j] = min(dp[i-1][j-1], dp[i - 1][j]) + A[i][j] dp[i][j]=min(dp[i1][j1],dp[i1][j])+A[i][j]
  • 注意到,有j=0时, d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i1][j1]是没有意义的(从最后一步分析可以看出),此时状态转移方程为(即在第i行的最左侧时,我们只能从第 i − 1 i-1 i1行的最左侧转移过来):
    d p [ i ] [ 0 ] = d p [ i − 1 ] [ 0 ] + A [ i ] [ 0 ] dp[i][0] = dp[i - 1][0]+ A[i][0] dp[i][0]=dp[i1][0]+A[i][0]
  • 注意到,当位于第i行的最右侧时,第i-1行少了一个元素(第 i 行有 i+1个元素,它们对应的 j 的范围为 [0, i])。即当 j = i j = i j=i时, d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j]没有意义,因此状态转移方程为(即当我们在第 ii 行的最右侧时,我们只能从第 i-1i−1 行的最右侧移动过来):
    d p [ i ] [ i ] = d p [ i − 1 ] [ i − 1 ] + A [ i ] [ i ] dp[i][i] = dp[i - 1][i - 1] + A[i][i] dp[i][i]=dp[i1][i1]+A[i][i]

(3)边界情况和初始条件

  • 初始情况: d p [ 0 ] [ 0 ] = A [ 0 ] [ 0 ] dp[0][0] = A[0][0] dp[0][0]=A[0][0]
  • 边界情况:无

(4)计算顺序

  • 从上到下,从左到右
    • dp[0][0]
    • dp[1][0]、dp[1][1]
    • dp[2][0]、dp[2][1]、dp[2][2]
    • dp[n-1][0]、dp[n-1][1]、dp[n-1][n-1]
  • 最终的答案即为 d p [ n − 1 ] [ 0 ] dp[n-1][0] dp[n1][0] d p [ n − 1 ] [ n − 1 ] dp[n-1][n-1] dp[n1][n1]中的最小值,其中 n 是三角形的行数。

在这里插入图片描述

class Solution {
public:
    /**
     * @param triangle: a list of lists of integers
     * @return: An integer, minimum path sum
     */
    int minimumTotal(vector<vector<int>> &triangle) {
        // write your code here
        int n = triangle.size();
        vector<vector<int>> f(n, vector<int>(n));
        f[0][0] = triangle[0][0];
        for (int i = 1; i < n; ++i) {
            f[i][0] = f[i - 1][0] + triangle[i][0];
            for (int j = 1; j < i; ++j) {
                f[i][j] = min(f[i - 1][j - 1], f[i - 1][j]) + triangle[i][j];
            }
            f[i][i] = f[i - 1][i - 1] + triangle[i][i];
        }
        return *min_element(f[n - 1].begin(), f[n - 1].end());
    }
};

当然,我们可以重用原数组

class Solution {
public:
    /**
     * @param triangle: a list of lists of integers
     * @return: An integer, minimum path sum
     */
    int minimumTotal(vector<vector<int>> &t) {
        if(t.empty()){
            return 0;
        }
        for (int i = 1; i < t.size(); ++i) {
            for (int j = 0; j < t[i].size(); ++j) {
                if(j == 0){
                    t[i][j] += t[i - 1][j];
                }else if(i == j){
                    t[i][j] += t[i - 1][j - 1];
                }else{
                    t[i][j] = std::min(t[i - 1][j - 1], t[i - 1][j]) + t[i][j];
                }
            }
        }



        int m = t.size();
        return *min_element(t[m - 1].begin(), t[m - 1].end());
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值