问题描述
给定一个三角形 triangle ,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。
示例 1:
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
解答
首先,既然是从上到下的路径,那么最后一个点必然是落在最后一行。
对于最后一行的某个位置的值,根据题意只能从上一行的某一个位置或者某两个位置之一转移而来。
同时,我们只关注前一位的累加值是多少,而不关心这个累加值结果是由什么路径而来的。
f[i][j] 代表到达某个点的最小路径和。
通过观察可以发现以下性质:
- 只要不是第一列(j!=0)位置上的数,都能通过「左上方」转移过来
- 只要不是每行最后一列(j!=i)位置上的数,都能通过「上方」转移而来
同时,这样的分析/转移过程,是可以推广并覆盖所有位置的。
至此,整个过程都没有问题,状态转移方程也能不重不漏的枚举到每一条路径。因此这个 DP 状态定义可用。
c
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize) {
int ** f = (int **)malloc(sizeof(int *)* triangleSize);
for(int i =0; i<triangleSize; i++){
f[i] = (int *)malloc(sizeof(int) * triangleColSize[i]);
}
f[0][0] = triangle[0][0];
int i, j;
for(i=1; i<triangleSize; i++){
for(j=0; j< triangleColSize[i]; j++){
f[i][j] = 10000;
//若不是j==0的位置,可以通过左上方转移
if(j!=0){
f[i][j] = fmin(f[i-1][j-1]+ triangle[i][j], f[i][j]);
}
if(j!=(triangleColSize[i]-1)){
f[i][j] = fmin(f[i-1][j]+ triangle[i][j], f[i][j]);
}
}
}
int ans = 1e9;
for(j =0 ;j<triangleColSize[triangleSize-1]; j++){
ans = fmin(ans, f[triangleSize-1][j]);
}
return ans;
}