zoukankan      html  css  js  c++  java
  • 【LeetCode】跳跃游戏

    给定一组非负整数,初始时处于数组的第一位下标 0 的位置,数组的每个元素代表那个位置可以跳跃的最大长度。判断你是否能够到达数组的最后一位下标。

    e.g. 

    A = [2, 3, 1, 1, 4],返回 true。

    A = [3, 2, 1, 0, 4],返回 false。

    我的想法是递归

    方法一:

     1 bool canJump(vector<int>& nums) {
     2     return jump(nums, 0);
     3 }
     4     
     5 bool jump(vector<int> &nums, int m) {
     6     int last = nums.size() - 1;
     7     if (m == last)   return true;
     8     int p = min(last, m + nums[m]);
     9     for (int i = p; i > m; i--) {    // 若不定义 p,i 初始为 m + nums[m] 并不会造成数组越界,但变量 p 减少了不必要的递归
    10         if (jump(nums, i))
    11             return true;
    12     }
    13     return false;
    14 }

    答案称这种方法是递归回溯法,时间复杂度为 O(2n),数据量很大时会超时。

    答案提供了多种解法

    方法二:

    自顶向下动态规划法(优化的回溯法)

        作如下定义:如果从数组中某一位置作为起始点,最终能够到达最后一个下标处,则把这个位置称为 “ Good Index ”,否则成为 “ Bad Index ”。因此这个跳跃问题就变成了判断 0 下标是不是一个 “ Good Index ”。

        用一个 memo 数组存储原数组每个下标是好的还是坏的,memo 数组元素的值是 GOOD、BAD、UNKNOWN 之一。

    e.g. 对于 nums = [2, 4, 2, 1, 0, 2, 0],

    Index 0 1 2 3 4 5 6
    nums 2 4 2 1 0 2 0
    memo G G B B B G G

    步骤:

    1. 初始时,memo 中所有元素都是 UNKNOWN,除了最后一个元素是 GOOD。
    2. 将回溯法中递归的第一步改为 “ 判断当前下标是否 UNKNOWN ”
      如果是 KNOWN,根据其值是 GOOD / BAD 返回 true / false;
      否则继续执行回溯。
    3. 一旦知道当前下标是好的还是坏的,将相应的值存在 memo 数组中。

    C++实现:

     1 enum Index {
     2     GOOD, BAD, UNKNOWN
     3 };
     4 
     5 vector<Index> memo;
     6     
     7 bool canJump(vector<int>& nums) {
     8     memo.reserve(nums.size());
     9     for (int i = 0; i < nums.size() - 1; i++) {
    10         memo[i] = UNKNOWN;
    11     }
    12     memo[nums.size() - 1] = GOOD;
    13     return jump(nums, 0);
    14 }
    15     
    16 bool jump(vector<int> &nums, int m) {
    17     if (memo[m] != UNKNOWN)
    18         return memo[m] == GOOD ? true : false;
    19     int p = min((int)nums.size() - 1, m + nums[m]);
    20     for (int i = p; i > m; i--) {
    21         if (jump(nums, i)) {
    22             memo[m] = GOOD;
    23             return true;
    24         }
    25     }
    26     memo[m] = BAD;
    27     return false;
    28 }

    对于数组中每个位置 i,我们在其右边 nums[i] 个元素中寻找 “ Good Index ”,因此这种方法的时间复杂度是 O(n2)。

    方法三:

    自底向上动态规划法

        将自顶向下 DP 的递归消除后就变成了自底向上 DP,这样就不会造成方法栈的过度开销。消除递归的方法是从数组最右边(倒数第二位)往左不断地递推 memo 数组的值。

    C++实现:

     1 enum Index {
     2     GOOD, BAD, UNKNOWN
     3 };
     4 
     5 vector<Index> memo;
     6     
     7 bool canJump(vector<int>& nums) {
     8     memo.reserve(nums.size());
     9     for (int i = 0; i < nums.size() - 1; i++) {
    10         memo[i] = UNKNOWN;
    11     }
    12     memo[nums.size() - 1] = GOOD;
    13     
    14     for (int i = nums.size() - 2; i >= 0; i--) {
    15         int p = min((int)nums.size() - 1, i + nums[i]);
    16         for (int j = p; j > i; j--) {
    17             if (memo[j] == GOOD) {
    18                 memo[i] = GOOD;
    19                 break;
    20             }
    21         }
    22     }
    23     
    24     return memo[0] == GOOD;
    25 }

    由于两种 DP 的原理相同,这种方法的时间复杂度也是 O(n2)。由于没有使用递归,终于 Accepted 了,但效率极低。

    最优方法:

    贪心法

        自底向上 DP 中,从后向前递推 memo 数组时,对于每一个下标 i,我们想知道是否能从这个位置到达一个 “ Good Index ”,而当我们找到了从 i 下标能到达的下标范围 ( i, p ] 内最右边的仅仅一个 “ Good Index ”(也可以选择最左边的一个)就把 i 设为 “ Good Index ”。因此,可以把从后向前追踪的一系列 “ Good Index ” 只用一个单独的变量存储,而不是记录在 memo 数组中。

    C++实现:

    1 bool canJump(vector<int>& nums) {
    2     int last = nums.size() - 1;
    3     for (int i = last; i >= 0; i--) {
    4         if (i + nums[i] >= last)
    5             last = i;
    6     }
    7     return last == 0;
    8 }

    也可以从前向后

    1 bool canJump(vector<int>& nums) {
    2     int n = nums.size(), reach = 0;    // reach 表示从 0 能到达的最远的下标
    3     for (int i = 0; i < n && i <= reach; i++) {
    4         reach = max(reach, i + nums[i]);
    5         if (reach >= n - 1)
    6             return true;
    7     }
    8     return false;
    9 }
  • 相关阅读:
    Nhibernate代码生成器v2.1中文版
    在asp.net中生成16位随机密码
    IIS 启动不了(发生意外错误0x8ffe2740)
    NET代码生成器
    Linux系统
    VS2005快捷键大全
    ASP+ACCESS数据库中文乱码问题解决
    如何配置ASP.NETOracle 9i 远程登陆数据库
    ASP.NET获取汉字拼音的首字母
    checkbox 实时操作,勾选后变色[带演示]
  • 原文地址:https://www.cnblogs.com/wayne793377164/p/7268492.html
Copyright © 2011-2022 走看看