写给远在巴蜀的你

版权声明:本文参考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;
}

此刻,我很想你。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值