矩阵的最小路径和 [DP]

来源:互联网 发布:软件测试原佩腾 编辑:程序博客网 时间:2024/03/29 07:34


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


【题目】

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

【举例】

如果给定的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: 01 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;}

继续刷题中。。。

0 0
原创粉丝点击