背包问题空间压缩含义

”明月如霜,好风如水,清景无限 “

时隔了不知道多少天,应该说最近都在忙于写毕设论文和相关代码。因此很久没更新了,先推荐一下关于西瓜书的两类视频:

一起啃书
机器学习实战
今天智能路障的鲁迅系列又更新了,心疼迅哥一秒,,,,

今天说的是动态规划最常见的一个题,题目不难,但想要把dp的空间压缩好好的说一下。

题目:给一个二维数组grid,里面正整数代表路径长度。求左上端到右下端的距离最小值。leetcode-64题。

例如:
nput:
[[1,3,1],
[1,5,1],
[4,2,1]]
Output: 7
因为:1->3->1->1->1

那么直接说一下思路。还是之前的三个类型可能用dp。种类数,最值和存在性。本题为最值,可以一试。

  • 1.状态选择很明显,因为求距离最小值,所以是距离,二维状态也很明显。即确定了dp[i][j]代表从左上角到(i,j)的最小距离。

  • 2.转移方程也很明显,dp[i][j] = min(dp[i-1][j] ,dp[i][j-1]) +grid[i][j]。

  • 3.边界问题,也挺明显的,因为是从左上角遍历到右下角,那么肯定要注意i0或者j0,因为转移方程中出现了i-1,或者j-1。

  • 4.最后是初值,或者说设计的dp内存。很明显dp[0][0] = grid[0][0]。如果你用二维的话,先取得行数为m,列数为n,那你想好的是,由于数组从0开始,你最后的输出应该是dp[i-1][j-1]。

上代码:

int m = grid.size(),n = grid[0].size();
        if (m == 0) return 0;
        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 && j ==0){
                    dp[i][j] = grid[0][0];
                }
                else if(i==0){
                    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 {
                    dp[i][j] = min(dp[i][j-1] ,dp[i-1][j]) + grid[i][j];
                }
            }
        }
        return dp[m-1][n-1];

然后就是空间压缩了。为了方便理解。你需要弄清前面的东西。你可以理解成空间压缩只是记录我们需要的值。我们需要的值是什么呢?

很简单,只要能让循环跑下去,能少就少。我们看看上面代码的循环发现:最终要的就是转移方程里面的dp[i][j] = min(dp[i][j-1] ,dp[i-1][j]) + grid[i][j];那么是不是我们只需要两个值,即dp[i][j-1]和dp[i-1][j]就可以了呢?其实文远也不清楚,只能说理论上可以,但是如果按照文远上面代码的遍历方式,从上往下一行又一行的话,应该是不行的。当然下面会举一个最简单的例子,可以只用两个。假设我们开两个变量a和b,这样赋值:

int a = dp[i][j-1];
int b = dp[i-1][j];

显然我们可以求出dp[i][j],问题是下一轮更新时,a = dp[i][j];但是我们没有dp[i-1][j+1],因此无法循环下去。

下面来看压缩成一行,dp长度为n。那么能循环下去吗?自行思考,答案是可以,我只说关键点,思路与二维一模一样,只不过是内存管理不一样。

因为,此时的长度为n的数组dp内部存放的是二维时的这些值:(当我们在求dp[j]时,此时压缩后数组内部对应压缩前如下)


代码如下:

      int m = grid.size(),n = grid[0].size();
        // if (m == 0) return 0;
        vector<int> dp(n,0);
        for (int  i = 0 ; i < m ; ++i){
            for(int j = 0 ; j < n ; ++j){
                if ((i == 0) && (j == 0)){
                    dp[j] = grid[i][j];
                }
                else if(i == 0 && j != 0){
                    dp[j] = dp[j-1] + grid[i][j];
                }
                else if(j == 0 && i != 0){
                    dp[j] = dp[j] + grid[i][j];
                }
                else{
                    dp[j] = min(dp[j],dp[j-1]) + grid[i][j];
                }
            }
        }
        return dp[n-1];

下面就是举个简单的空间压缩当两个的例子,当然是入门的斐波那契数列。转移方程:dp[i] = dp[i-1] + dp[i-2]

int a=1,b=1;
for(int i = 0 ; i < n ;++i){
  a = b;
  b = a+b
}
return a;

例子很简单,但是你想想你用递归,相对dp有很多重复,而dp在转移方程只与前两个数有关时,很明显可以只存两个状态,并不停的替换,即循环可以运转,不缺值。
最后说一下希望大家推转移方程时,推不出来可以特例,写几个。从特殊到一般。最后再提一嘴,上面这还能矩阵快速幂求。

END

文远的学习笔记

作者:不爱跑马的影迷不是好程序猿

   喜欢的话请关注点赞👇 👇👇 👇                     

在这里插入图片描述

壹句:当我沉默的时候,我感觉充实;我将开口,同时感到空虚。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值