动态时间规整_动态规划-数组系列(10%)

32d4b92a8deec36b55c04110e55f5636.png

哇呀呀呀呀~~~好!实不相瞒,小弟我就是人称玉树临风胜潘安,一支梨花压海棠的小淫虫周伯通!《唐伯虎点秋香》

不同路径 II​leetcode-cn.com

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

c8cb95ce7ab720b5098c5bed535f3c5e.png
输入:
[
  [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算法。实际上这种算法只要是做时间序列的分类聚类,最优的算法基本上都是在此基础之上,而这个算法的基础就是最短路径。

2d0844ebc53680930f3ff808d37f2cb8.png

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值