对给定高度为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
1 <= triangle.length <= 200 triangle[0].length == 1 triangle[i].length == triangle[i - 1].length + 1 -104 <= triangle[i][j] <= 104
想用动态规划写出来,重点在于状态转移方程。
将等腰三角形抽象为等腰直角三角形,如下
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];
三种情况:
三角形左路,在直角图里就是第一列,满足:
dp[i][0]=dp[i-1][0];
三角形右路,在直角图里是对角线,满足:
dp[i][i]=dp[i-1][i-1]+a[i][i]
普通位置
dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+a[i][j];
这样程序就很好写了。就是往dp数组里填数就行,最后筛出最后一行的最小值就行。
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; } };
如果要记下路径,需要再来一个二维数组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];
逐个出栈。
//三角形最小路径 #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; }