算法学习->求解三角形最小路径及其值

2021/11/5 14:10:48

本文主要是介绍算法学习->求解三角形最小路径及其值,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

 

00 问题

00-1 描述

对给定高度为n的一个整数三角形,找出从顶部到底部的最小路径和。每个整数只能向下移动到与之相邻的整数。

找到一个一样的力扣题:120. 三角形最小路径和 - 力扣(LeetCode) (leetcode-cn.com)

示例1:
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
4
7
8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
    
示例2:
输入:triangle = [[-10]]
输出:-10

00-2 提示:

1 <= triangle.length <= 200
triangle[0].length == 1
triangle[i].length == triangle[i - 1].length + 1
-104 <= triangle[i][j] <= 104

01 思路

想用动态规划写出来,重点在于状态转移方程。

将等腰三角形抽象为等腰直角三角形,如下

  0 1 2 3

3 4
5 7
3 9 2

加上下标化的序列,我们就可以用二维数组dp来考虑。dp是用来存储到i,j位置后用到的最短路径长度,比如dp[2] [2]=2+4+7=13

定义一个起点:

dp[0][0] = a[0][0];

三种情况:

  1. 三角形左路,在直角图里就是第一列,满足:

    dp[i][0]=dp[i-1][0];
  2. 三角形右路,在直角图里是对角线,满足:

    dp[i][i]=dp[i-1][i-1]+a[i][i]
  3. 普通位置

    dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+a[i][j];

这样程序就很好写了。就是往dp数组里填数就行,最后筛出最后一行的最小值就行。

02 代码

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int len = triangle.size();
        int dp[200][200]={0};
        dp[0][0]=triangle[0][0];
        for(int i=1;i<len;i++){
            dp[i][0] = dp[i-1][0]+triangle[i][0];
        }
        for(int i=1;i<len;i++){
            dp[i][i] = triangle[i][i]+dp[i-1][i-1];
        }
        for(int i=2;i<len;i++){
            for(int j=1;j<i;j++){
                dp[i][j] = triangle[i][j]+min(dp[i-1][j], dp[i-1][j-1]);
            }
        }
        //填充dp
        //下面筛选路径最短
        int ans = dp[len-1][0];
        for(int j = 1;j < len;j++){
            if(dp[len-1][j]<ans){
                ans = dp[len-1][j];
            }
        }
        return ans;
    }
};

03 升级版--记录路径

03-1 思路

如果要记下路径,需要再来一个二维数组pre来记录坐标为i,j的点的前一个节点。那么如何记录呢?我们看一下:

  • (i,i)的前一个节点就是(i-1,i-1);

  • (i,j)的前一个节点是(i-1,j)或者(i-1,j-1);

  • (i,0)的前一个节点是(i-1,0)。

容易从这些情况总结出,上一节点一定为i-1,只需记录j的值即可。故我们在pre二维数组里记录的就是当前节点的前一节点的j值。

记录之后,我们还需要输出这个最小路径。怎么输出呢?

我们在前一问题的基础上得到最后行的最小值的列值后,将它返回主控函数,并用它作为参数给路径输出函数Disppath。

输出方法为:

  • 对于当前节点,入栈,查它的pre数组值,更新,继续该操作,直到完成。

    更新操作为:

     path.push_back(a[i][k]);
     k=pre[i][k];
  • 逐个出栈。

 

03-2 代码

//三角形最小路径
#include<stdio.h>
#include<vector>
#include<string.h>
using namespace std;
#define MAXN 100
int a[MAXN][MAXN];//三角形
int n;//高度
int ans = 0;//应求的路径长度
int dp[MAXN][MAXN];
int pre[MAXN][MAXN];//前驱结点
//根据状态转移方程,只记录列号即可
int Search(){
    int i,j;
    dp[0][0] = a[0][0];
    for(i=1;i<n;i++){
        dp[i][0]=dp[i-1][0]+a[i][0];
        pre[i][0]=i-1;
    }
    for(i=1;i<n;i++){
        dp[i][i]=dp[i-1][i-1]+a[i][i];
        pre[i][i]=i-1;
    }
    for(i=2;i<n;i++){
        for(j=1;j<i;j++){
            if(dp[i-1][j-1]<dp[i-1][j]){
                dp[i][j]=dp[i-1][j-1]+a[i][j];
                pre[i][j]=j-1;
            }
            else{
                dp[i][j]=dp[i-1][j]+a[i][j]; 
                pre[i][j]=j;           
            }
        }
    }
    ans = dp[n-1][0];
    int k=0;
    for ( j = 1; j < n; j++)
    {
        if(ans>dp[n-1][j]){
            ans = dp[n-1][j];
            k=j;
        }
    }
    return k; 
}
void Disppath(int k){
    int i=n-1;
    vector<int>path;//路径节点
    while(i>=0){
        path.push_back(a[i][k]);
        k=pre[i][k];
        i--;
    }
    vector<int>::reverse_iterator it;
    for(it=path.rbegin();it!=path.rend();++it){
        printf("%d ",*it);
    }
    printf("\n");
}
​
int main(){
    int k;//k行k列的三角形
    memset(pre, 0, sizeof(pre));
    memset(dp, 0, sizeof(dp));
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        for(int j=0;j<=i;j++){
            scanf("%d",&a[i][j]);
        }
    }
    k=Search();
    Disppath(k);
    printf("%d\n",ans);
    return 0;
}
​

 



这篇关于算法学习->求解三角形最小路径及其值的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程