[24]跳跃游戏和不同路径

*内容来自leetcode,今天起进入了中级算法的动态规划部分

1.跳跃游戏

题目要求

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

示例 1:

输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:

输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

提示:

  • 1 <= nums.length <= 3 * 104
  • 0 <= nums[i] <= 105

思路

从第一个下标跳至最后一个下标,只能往前跳或者原地不动,无论如何如果能够达到终点,最后跳跃的长度总归是n-1。处于任意一个位置时,可以确定能够跳跃的范围,如果最后范围能够囊括终点,则可以抵达。就有了如下的代码,如下代码对于简单数组是能够完成任务的,但是用例用有的例子过于复杂,存在如下的步数过长的问题,导致代码运行超时。。。

[6283,7996,1198,4197,8108,14510,9487,1502,7142,17669,4166,6511,7171,11336,11534,13144,10082,7433,8029,18659,2884,9662...]

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        n = len(nums)
        check = [0] * (n)
        reached = [0]
        while reached:
            loc = reached.pop(0)
            if loc == n-1:
                return True
            step = nums[loc]
            while step > 0:
                if loc + step < n and check[loc+step] == 0:
                    reached.append(loc + step)
                    check[loc + step] = 1
                step -= 1
        return False

在第二个循环里增加了一个判断,居然通过了,只是耗时非常感人。

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        n = len(nums)
        check = [0] * (n)
        reached = [0]
        while reached:
            loc = reached.pop(0)

            step = nums[loc]
            while step >= 0:
                if loc + step == n-1:
                    return True
                if loc + step < n and check[loc+step] == 0:
                    reached.append(loc + step)
                    check[loc + step] = 1
                step -= 1
        return False

考虑换一种思路,之前做过的爬楼梯的题目,当前阶的前一步总是上一阶或者上上阶 ,由此可以一步一步往前回溯。这道题也可以用类似的思路,从能够抵达终点的点开始往前倒,如果最终能够倒回起点,则说明能够从起点到终点。写出来超时。。。

官方给出的思路是维护最大可达距离,如果最大可达超过最后一个数的位置,则说明可达,如果到最后还是小于最后一个数的位置,则说明不可达。

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        n = len(nums)
        max_reach = 0
        for i in range(0,n):
            if max_reach >= n-1:
                return True 
            if max_reach >= i and max_reach < i + nums[i]:
                max_reach = i + nums[i]
        return False

2.不同路径

题目要求

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

示例 1:

输入:m = 3, n = 7
输出:28

示例 2:

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

思路

m为行,n为列。无论怎么走,向右的步数总为n-1,向下的步数总为m-1,不同的路径就在于向下和向右的搭配不同。以一个数组来看,向下为1,向右为2,那么数组中1和2的个数是一定的,只是顺序问题,所以只需要找到全部的排序方式就可以了,在数学上来说就是一个排列组合问题。如果需要每一个路径的全部过程,可以考虑回溯算法,但是此题只需要总的路径数。

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        def jieC(num):
            res = 1
            #此处开始用的是while循环来实现,但是时间花费会更大
            #可能是用while操作更多
            for i in range(1,num+1):
                res = res * i
            return res
        routeNum = 0
        total = m + n -2 
        routeNum = jieC(total) // (jieC(n-1)*jieC(m-1))
        return routeNum

根据官方的基于动态规划的解答,本题可以视为是类似于爬楼梯的二维版本。

核心在于:f(i,j)=f(i−1,j)+f(i,j−1)

具体的:

我们用 f(i,j)表示从左上角走到 (i,j)的路径数量,其中 iii 和 jjj 的范围分别是 [0,m)和 [0,n)。

由于我们每一步只能从向下或者向右移动一步,因此要想走到 (i,j),如果向下走一步,那么会从 (i−1,j)(i-1, j)(i−1,j) 走过来;如果向右走一步,那么会从 (i,j−1)走过来。因此我们可以写出动态规划转移方程:

f(i,j)=f(i−1,j)+f(i,j−1) 
需要注意的是,如果 i=0,那么 f(i−1,j)并不是一个满足要求的状态,我们需要忽略这一项;同理,如果 j=0,那么 f(i,j−1)并不是一个满足要求的状态,我们需要忽略这一项。

初始条件为 f(0,0)=1,即从左上角走到左上角有一种方法。

最终的答案即为 f(m−1,n−1)。

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        route = [[1 for _ in range(n)]] + [[1] + [0]*(n-1) for _ in range(m-1)]
        for i in range(1,m):
            for j in range(1,n):
                route[i][j] = route[i-1][j] + route[i][j-1]
        return route[m-1][n-1]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值