zoukankan      html  css  js  c++  java
  • LeetCode #55. Jump Game 数组 贪心 线性DP 回溯

    Description


    Given an array of non-negative integers, you are initially positioned at the first index of the array.

    Each element in the array represents your maximum jump length at that position.

    Determine if you are able to reach the last index.

    Example 1:

    Input: [2,3,1,1,4]
    Output: true
    Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
    

    Example 2:

    Input: [3,2,1,0,4]
    Output: false
    Explanation: You will always arrive at index 3 no matter what. Its maximum
                 jump length is 0, which makes it impossible to reach the last index.
    



    思路


    解法一

    贪心策略。

    使用一个变量 dist 记录当前可到达的最远距离,每次到第 i 个位置时,让可达到的距离最远。最后判断是否可到达的最远距离 ≥ 最后一个元素的索引,如果是,则说明能够 jump 到 nums 的最后一个位置。

    在每一次贪心的过程中,还需要判断能不能翻过中途遇到的 0。如果不能翻过去,说明肯定不能走到 nums 的最后一个位置了,此时需要立即停止遍历 nums,结束贪心。给出一个典型的例子:

    [1, 0, 2, 3]
    false
    

    另外,第一次跑代码的时候被 corner case 坑得不轻,注意仅有单个元素以及首元素为 0 的 corner case,比如:

    [0]
    True
    
     [0, 1, 2, 3]
    false
    

    时间复杂度:O(n)
    空间复杂度:O(1)

    耗时 8 ms, faster than 94.49%, Memory 7.8 MB

    class Solution {
    public:
        bool canJump(const vector<int> &nums) {
            if (nums.size() < 2) return true;
    
            int dist = INT_MIN;  // longest distance that can reach from current position
            int end = nums.size() - 1;
            for (int i = 0; i < end; ++i) {
                // detect if we will be stopped by 0
                if (!nums[i] && dist <= i) {
                    break;
                }
                
                // update longest distance jump from i
                if (dist < i + nums[i]) {
                    dist = i + nums[i];
                }
    
                if (dist >= end) return true;
            }
            
            return false;
        }
    };
    



    解法二

    类似“跳台阶”问题,很自然地就想到了线性DP。

    设状态 dp[i] 为到达 i 时剩余的可跳跃距离。

    为什么是“剩余的可跳跃距离”呢?因为在第 i 位置时,nums[i] 不一定比 nums[0..i-1]之前的值大很多,这意味着可能可以从 nums[0..i-1] 直接跳到 nums[i+1],而略过 nums[i]。比如,[5, 2, 0, 3]。所以,dp[i] 的取值可能是 dp[i-1] 减掉 i-1 到 i 花费的 1 个距离,或者是 nums[i]。

    因此状态转移方程为 dp[i] = max(dp[i-1] - 1, nums[i])。

    由于每次状态转移时 dp[i] 只会使用到记忆化数组中的 dp[i-1],所以下面的代码其实还可以优化一下空间,利用单变量 prev 存储 dp[i-1] 的值就行,而不是一个数组存储 dp[0..n]。

    corner case 的处理方法和解法一是一样的。

    时间复杂度:O(n)
    空间复杂度:O(n)

    耗时 8 ms, faster than 94.49%,Memory 8.2 MB

    class Solution {
    public:
        bool canJump(const vector<int> &nums) {
            if (nums.size() < 2) return true;
    
            // cannot jump to any position if first element is 0
            if (!nums[0]) return false;
    
            // dp[i] represents the remaining jump-able distance when reaching i
            vector<int> dp(nums.size());
            dp[0] = nums[0];
            
            //dp[i] = max(dp[i-1], nums[i]) - 1
            //dp[i] < 0
            for (int i = 1; i < nums.size() - 1; ++i) {
                dp[i] = max(dp[i-1] - 1, nums[i]);
    
                if (dp[i] <= 0) return false;  // cannot jump to next position
            }
            
            return true;
        }
    };
    



    解法三

    暴力dfs。



    参考




  • 相关阅读:
    MySQL创建用户和权限管理
    自建单机MySQL数据库备份方案
    Elasticsearch 6.8.4 启动报错解决方法
    自定义 Generate POJOs.groovy
    SpingBoot —— RestTemplate的配置
    springboot —— 多数据源
    SpringBoot -- 计划任务
    SpingBoot —— 多线程
    SpringBoot -- 事件(Application Event)
    SpringBoot —— AOP注解式拦截与方法规则拦截
  • 原文地址:https://www.cnblogs.com/Bw98blogs/p/12747922.html
Copyright © 2011-2022 走看看