”明月如霜,好风如水,清景无限 “
时隔了不知道多少天,应该说最近都在忙于写毕设论文和相关代码。因此很久没更新了,先推荐一下关于西瓜书的两类视频:
一起啃书
机器学习实战
今天智能路障的鲁迅系列又更新了,心疼迅哥一秒,,,,
壹
今天说的是动态规划最常见的一个题,题目不难,但想要把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
作者:不爱跑马的影迷不是好程序猿
喜欢的话请关注点赞👇 👇👇 👇
壹句:当我沉默的时候,我感觉充实;我将开口,同时感到空虚。