版权声明:本文参考CSDN博主「hang-7788」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hang404/article/details/85279268
题目意思是一个m*n的矩阵,求从左上角到右下角的最短路径,只能向右走或是向下走。
首先证明这一题若使用贪心算法,可能会得到不正确的结果,例1如下:
若采用贪心算法,在(0,0)处会选择向下走,但最优路径应为1->9->1->1,而不是1->6->10->1
本题可以这样分析,仍以上表为例,需要得到到达右下角(1,2)的最短距离,则要找的其实是(1,1)和(0,2)的最小值再加上右下角(1,2)的值。这样要求右下角(1,2)的最小值的问题即转换成了求min((1,1),(0,2))的问题。
接下来,我们用dp(i,j)来代表从左上角(0,0)到(i,j)这个位置的最短距离,用arr代表上述的二维数组,那么上述的描述
右下角(1,2)的最小值的问题即转换成了求min((1,1),(0,2))的问题
即转换成了以下表达式:
dp(1,2) = min(dp(1,1),dp(0,2)) + arr[1][2]
接下来,我们要求的其实就是ddp(1,1)和dp(0,2),然后比一下谁小,再加上arr[1][2]就行了。
类似于这样的,将大的问题分解成若干个规模较小(小到很容易解决的程序)的子问题。然后自底向上,先求解最小的子问题,把结果存在表格中,在求解大的子问题时,直接从表格中查询小的子问题的解,避免重复计算,从而提高算法效率。我们叫做动态规划。得到的类似于dp(1,2) = min(dp(1,1),dp(0,2)) + arr[1][2]这样的表达式,我们称之为状态转移方程,这也是动态规划的核心。
与分治算法不同的是,分治算法是把原问题分解为若干个子问题,自顶向下求解子问题,合并子问题的解,从而得到原问题的解。
那么在实际使用dp的过程中通常使用自底向上的方法进行求解,回到上述问题,虽然我们的状态转移方程是反推得到的,但是在实际的计算过程中,我们是正推的,什么意思呢?
dp(1,2) = min(dp(1,1),dp(0,2)) + arr[1][2]
这样一个方程,在实际计算中,我们在计算dp(1,2)的时候必须已经得到了dp(1,1)和dp(0,2)的值,继续推,dp(1,1)的值又由dp(0,1)和dp(1,0)得到,那么dp(0,1)和dp(1,0)的值其实都可以直接根据dp(0,0)的值得到了。我们发现,在计算最短距离的过程中,第一行和第一列只和一个数相关,即第一列只和上一行相关,第一行只和上一列相关,所以,我们可以在dp求解之前对其进行适当的初始化。同事我们将上述的状态转移方程一般化,即得到:
dp(i,j) = min(dp(i,j-1),dp(i-1,j)) + arr[i][j]
需要注意的是,i、j和数组的边界问题。
经过上述的观察,很明显的,dp应由一个和输入的arr数组一样大小的数组表示,然后在按照第一行和第一列只和一个数相关,即第一列只和上一行相关,第一行只和上一列相关进行初始化,对上述arr数组:
初始化的dp数组为
*表示未计算得到结果,接下来我们计算两处*代表的值,第一处*即为dp[1][1]的值,我们按照状态转移方程来递推dp[1][1]的值,代入状态转移方程
dp(i,j) = min(dp(i,j-1),dp(i-1,j)) + arr[i][j]
dp[1][1]=min(dp[1][0],dp[0][1])+arr[1][1]=min(7,10)+10=17
dp[1][2]=min(dp[1][1],dp[0][2])+arr[1][2]=min(17,11)+1=12
不难看到,到这里,我们已经求出了我们想要的解,即dp[1][2]=12,具体的实现如下:
#include <stdio.h>
#include <stdlib.h>
#define min(a, b) ((a) < (b) ? (a) : (b))
int minPathSum(int** dp, int row, int col) {
if(row == 0 || col == 0)
return 0;
int i = 0, j = 0;
for(i = 1; i < row; i++)
dp[i][0] += dp[i - 1][0];
for(i = 1; i < col; i++)
dp[0][i] += dp[0][i - 1];
for(i = 1; i < row; i++) {
for(j = 1; j < col; j++) {
dp[i][j] += min(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[row - 1][col - 1];
}
int main(){
int **arr,row,col;
scanf("%d %d",&row,&col);
arr = (int**)malloc(sizeof(int*)*row);
for(int i = 0;i < row;i++){
arr[i] = (int *)malloc(sizeof(int)*col);
for(int j = 0;j < col;j++){
scanf("%d",&arr[i][j]);
}
}
printf("%d\n",minPathSum(arr,row,col));
free(arr);
return 0;
}
此刻,我很想你。