64.Minimum Path Sum

动态规划经典题目:
题目描述:
给定一个m行n列的矩阵grid,从左上角开始走,每次只能往下边或者往右边走,不能走出矩阵,求最后我们走到右下角经过的路线的最小值。

分析题目:
我们每次只能走右或者下,那么你在这两种选择之间一定会有一个最优解,也就是最短的路程。这里我们假设一个m×n的矩阵来存储所有的位置的最优解。
1.初始值:dp[0][0] = grid[0][0]
2.很简单的是从上面走的话,那就是一条路走到最末尾,所以这些位置上的最优解就是直接dp[0][j] = dp[i][j-1] + grid[0][j]
同理,从最左边走的话也是一条路到头,也就是dp[i][0] = dp[i-1][0] + grid[i][j]
3.其他位置,也就是中间的那些,dp[i][j] = min(dp[i-1][j],dp[i][j-1]) + grid[i][j],它只依赖与左边或者上面来的最优解。
上面这些大概看完,就可以看代码了:

1.动态规划

//时间复杂度O(m*n),空间复杂度O(m*n)
int minPathSum(vector<vector<int> >& grid) {
        //使用一个二维数组保存所有位置的最优解
        int m = grid.size();
        int n = grid[0].size();
        vector<vector<int> > dp(m,vector<int>(n));

        for(int i = 0;i < m;i++)
        {
                for(int j = 0;j < n;j++)
                {
                        if(i == 0){
                                if(j == 0){//dp[0][0]
                                        dp[i][j] = grid[i][j];
                                }
                                else{//第一种情况,往右走到头
                                        dp[i][j] = dp[i][j-1] + grid[i][j];
                                }
                        }
                        else if(j == 0){//第二种情况,往下走到头
                                dp[i][j] = dp[i-1][j] + grid[i][j];
                        }
                        else{
                                //cout<<"min is "<<min(dp[i-1][j],dp[i][j-1])<<endl;
                                dp[i][j] = min(dp[i-1][j],dp[i][j-1]) + grid[i][j];
                        }
                }
        }
        return dp[m-1][n-1];
    }

2.优化空间的动态规划
将二维数组dp缩减为一维数组。
上面这种方法,我们将所有位置的最优解的情况都保存在了一张m×n的表中,但其实是只要我们求得最后一个数dp[m-1][n-1]即可,我们可以求出来最后一行或者最后一列,只要保证我们能走到最后的第m行或者第n列就可以找到右下角的元素。
这里我们上面是对行数m进行循环,那咱们就以一行来不断刷新,声明一个有n个元素的数组dp,也就是dp[n],针对m不断轮询,刷新它的值,具体流程大概画了一张图片方便理解:
这里写图片描述

下面就是代码了:

//时间复杂度O(m*n),空间复杂度O(n)
int minPathSum_signal(vector<vector<int> >& grid) {
        int m = grid.size();
        int n = grid[0].size();
        vector<int> dp(n);
        for(int i = 0;i < m;i++)
        {
                for(int j = 0;j < n;j++){
                        if(i == 0){
                                if(j == 0){
                                        dp[j] = grid[i][j];
                                }
                                else{
                                        dp[j] = dp[j-1] + grid[i][j];
                                }
                        }
        //这里该怎么理解呢?第一次,也就是上面这个循环,更新出来了第一组dp
        //之后下面首先是得到一个刷新的dp[0] = dp[0] + grid[1][0],由于j==0这一行全部都是这么计算,就是上面的数一直加到下去,所以这里不需要更改
        //得到新的dp[0],现在相当与dp[0]往下走,dp[1]往右走,新的dp[1]就取决于dp[0] 和 dp[1] 中的最优解,更新dp[1]
        //更新了dp[1],之后,j++,现在是dp[1](新的) 和 dp[2](老的),取最优解,更新dp[2],这里就更新完第二组了
        //dp[n] = {2,101,201}

        //同理,dp[0] = dp[0] + grid[2][0],之后照旧继续循环
                        else if(j == 0){
                                dp[j] = dp[j] + grid[i][j];
                        }
                        else{
                                dp[j] = min(dp[j-1],dp[j]) + grid[i][j];
                        }

                }
        }
        for(int i = 0;i < n;i++)
        {
                cout<<dp[i]<<"\t";
        }
        cout<<endl;

        return dp[n-1];
}

本来这个博客是早就发了的,但是不小心点了编辑已经发表过的文章,然后一删除保存,全没了–| _ |–又重新理了下思路,重新写了一边,也算是教训吧,以后要多注意了.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值