矩阵的最小路径和 [DP]

    xiaoxiao2021-12-15  62

    从本文开始,我打算多刷一些动态规划的题。不仅如此,各种典型算法也会在分类刷一刷。

    【题目】

    给定一个矩阵,从左上角开始每次只能向右或者右下走,最后到达右下角的位置,路径上所有数字累加起来就是路径和,返回所有路径中最小的路径和。

    【举例】

    如果给定的m如下:

    1,3,5,9

    8,1,3,4

    5,0,6,1

    8,8,4,0

    路径1,3,1,0,6,1,0是所有路径中路径和最小的,所以返回12。

    【解答】

    这道题是很明显的动态规划算法,动态规划算法的一般步骤就是列一张表来存放计算过的值,通常采用数组实现。这道题的特殊之处在于表的值不仅和自身有关,而且和原矩阵也是相关的。表中的值需要和原矩阵中的值相加,因为我们求的是路径和。

    矩阵如下,只画了一部分,动态规划通常会申请+1的空间,i=0和j=0用来存放动态规划的初始值,这里我们赋为0,这张表是B[m+1][n+1]。

    :0 1 2 3 4 

    0: 0 0 0 0 0

    1: 0 1 9 18

    2: 0 9 5

    ...

    不难看出,B[1][j]和B[i][1]是取它上下两个相邻值再加上原数组A[i-1][j-1]的值(看1和4),为什么减一是因为规划表真实数据从1开始而不是从0开始。这些其实是为了初始化下标为1的这些点。

    除了下标为i=1或j=1的部分,其他值(看5)是上下相邻两个值得最小值与原数组A[i-1][j-1]的值相加。这就是dp数组的公式了。

    我第一种解法就没有初始化,直接放在循环里面做了,不过效率肯定不高,下面是我第一种解法,可以和第二种对比一下。

    for(int i=1; i<=m; ++i){ for(int j=1; j<=n; ++j){ if(i == 1 || j == 1) B[i][j] = std::max(B[i-1][j], B[i][j-1]) + A[i-1][j-1]; else B[i][j] = std::min(B[i-1][j], B[i][j-1]) + A[i-1][j-1]; } }

    其实最早的第一次解我都没有考虑到下标为1的应该是max,当时我直接只用了一个min,然后求得结果为11,还好我认识到了错误。

    起始也不用std::max,直接相加就行了

    下面看第二种完整代码,注意第二种只是参考,还有第三种,我没有删除这个是为了警示我自己考虑要周全,突然又想到的。

    int min_path(const std::vector<std::vector<int>>& A) { if(A.size() == 0) return 0; int m = (int)A.size(); int n = (int)A[0].size(); std::vector<std::vector<int>> B(m+1, std::vector<int>(n+1, 0)); for(int i=1; i<=m; ++i) B[i][1] = B[i-1][1] + A[i-1][0]; for(int j=1; j<=n; ++j) B[1][j] = B[1][j-1] + A[0][j-1]; for(int i=2; i<=m; ++i){ for(int j=2; j<=n; ++j){ B[i][j] = std::min(B[i-1][j], B[i][j-1]) + A[i-1][j-1]; } } return B[m][n]; }

    第三种:分配同样的大小就可以了,不需要i-1和j-1了。

    int min_path(const std::vector<std::vector<int>>& A) { if(A.size() == 0) return 0; int m = (int)A.size(); int n = (int)A[0].size(); std::vector<std::vector<int>> B(m, std::vector<int>(n, 0)); B[0][0] = 1; for(int i=1; i<m; ++i) B[i][0] = B[i-1][0] + A[i][0]; for(int j=1; j<n; ++j) B[0][j] = B[0][j-1] + A[0][j]; for(int i=1; i<m; ++i){ for(int j=1; j<n; ++j){ B[i][j] = std::min(B[i-1][j], B[i][j-1]) + A[i][j]; } } return B[m-1][n-1]; } 第三种直接分配相同大小的空间就可以了。唉,动态规划分配空间就是要么加1,要么不加1,我还是不够熟练。

    测试用例:

    int main() { std::vector<std::vector<int>> A = { {1, 3, 5, 9}, {8, 1, 3, 4}, {5, 0, 6, 1}, {8, 8, 4, 0}}; int find = min_path(A); std::cout<<"the min path is : "<<find<<std::endl; typedef std::vector<std::vector<int>>::iterator iterator1; typedef std::vector<int>::iterator iterator2; for(iterator1 iter= A.begin(); iter!=A.end(); ++iter){ for(iterator2 it = (*iter).begin(); it!=(*iter).end(); ++it) std::cout<<*it<<' '; std::cout<<std::endl; } return 0; } 继续刷题中。。。

    转载请注明原文地址: https://ju.6miu.com/read-1000001.html

    最新回复(0)