哇呀呀呀呀~~~好!实不相瞒,小弟我就是人称玉树临风胜潘安,一支梨花压海棠的小淫虫周伯通!《唐伯虎点秋香》
不同路径 IIleetcode-cn.com一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
这道题只是在原来的基础上进行了点修改,可以参看我之前的动态规划一;而现在为什么改名字了,主要是看到有大佬的刷题路线,各个动态规划的组成部分。之后都会根据这个分类进行刷题总结,这样也能有更深刻的体会,也希望大家能有所收获。天大地大,刷题不断。
在刷了这个系列很多之后,一开始就是按照固定的模板,定义数组,初始化,状态转移,答案。现在终于觉得自己的代码有些冗余了,然后开始慢慢改进。这道题变得是加了一个复杂物体,那么知道这个位置路径为0,也就是不可能通过。按照之前的写法如下:
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
if not obstacleGrid or not len(obstacleGrid[0]):
return 0
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [[0] * n for _ in range(m)]
if obstacleGrid[0][0] == 0:
dp[0][0] = 1
for i in range(1, n):
if obstacleGrid[0][i] == 1:
dp[0][i] = 0
else:
dp[0][i] = dp[0][i-1]
for i in range(1, m):
if obstacleGrid[i][0] == 1:
dp[i][0] = 0
else:
dp[i][0] = dp[i-1][0]
for i in range(1, m):
for j in range(1, n):
if obstacleGrid[i][j] == 0:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
else:
dp[i][j] = 0
return dp[-1][-1]
完整的按照之前的套路,当我在做初始化的时候,开始发现这样拆开写有时候确实很冗余;我们可以尝试把初始化放在内部,并且尝试为什么可以放在内部。比如初始化i=0,j=0是容易理解的就是判断是不是原地就是坑,然后看i=0的时候,在计算dp[i][j] = dp[i][j-1]的时候,就是看看前面有没有时候,只要有一个时候,那石头后的都为0;i==0,j==0已经判断了,那么现在计算dp[i][j-1]的时候是合法的,同样道理对于计算dp[i-1][j]。看起来很精妙实际上也是按照思路来的,对于双重循环肯定是先遍历最里面的,因此就固定初始化第一行。
改进后的:
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
if not obstacleGrid or not len(obstacleGrid[0]):
return 0
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [[0] * n for _ in range(m)]
for i in range(m):
for j in range(n):
if obstacleGrid[i][j] == 0:
if i == 0 and j == 0:
dp[0][0] = 1
elif i == 0:
dp[i][j] = dp[i][j-1]
elif j == 0:
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
else:
dp[i][j] = 0
return dp[-1][-1]
不过运行的实际结果是,第一个击败了90%左右的,第二个只击败了50%左右的。这里讲到路径的条数或者最短路径,想起了之前做时间序列分析的时候遇到的DTW算法。实际上这种算法只要是做时间序列的分类聚类,最优的算法基本上都是在此基础之上,而这个算法的基础就是最短路径。
DTW(Dynamic Time Warping)动态时间规整
DTW可以计算两个时间序列的相似度,尤其适用于不同长度、不同节奏的时间序列(比如不同的人读同一个词的音频序列)。DTW将自动warping扭曲 时间序列(即在时间轴上进行局部的缩放),使得两个序列的形态尽可能的一致,得到最大可能的相似度。
传统的都是基于欧式距离的一对一对齐,只要一个位置稍稍移动,得到的结果就会变得很差,而这种算法就是为了解决这个问题。
实际的代码:
function dist = dtw(t,r)
n = size(t,1);
m = size(r,1);
% 帧匹配距离矩阵
d = zeros(n,m);
for i = 1:n
for j = 1:m
d(i,j) = sum((t(i,:)-r(j,:)).^2);
end
end
% 累积距离矩阵
D = ones(n,m) * realmax;
D(1,1) = d(1,1);
% 动态规划
for i = 2:n
for j = 1:m
D1 = D(i-1,j);
if j>1
D2 = D(i-1,j-1);
else
D2 = realmax;
end
if j>2
D3 = D(i-1,j-2);
else
D3 = realmax;
end
D(i,j) = d(i,j) + min([D1,D2,D3]);
end
end
dist = D(n,m);
而它核心的实际上就是D(i,j)=d(i,j)+min([D1,D2,D3])一句,就和维特比算法一样,如果只是放在动态规划里,实际上大家觉得都有些low了,对应到力扣上也就中等难度。但是难的是将这种方法用在特定的问题上,并且优秀地解决了大问题。值得一提的是维特比算法也是动态规划的思想,大概和之前动态规划系列-4里的三角形里的最小值一样,而维特比则是寻找最大概率。
这两个算法在各自领域的影响实在是太大了,不得不让我对学习动态规划有了更深刻的认识。
参考博客:
所有题目均来自于力扣
https://zhuanlan.zhihu.com/p/32849741
https://www.cnblogs.com/luxiaoxun/archive/2013/05/09/3069036.html