矩阵的最小路径和 [DP]


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


【题目】

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

【举例】

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

继续刷题中。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值