leetcode 55. 跳跃游戏 及 45. 跳跃游戏 II

55. 跳跃游戏

问题描述

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

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

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:

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

代码1(回溯法)

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int n = nums.size();
        return Jump(nums,0,n);
    }
    bool Jump(vector<int>& nums,int position,int n)
    {
        if(position == n-1)return true;
        else if(position > n-1)return false;
        int furthermove = min(n-1,position+nums[position]);
        //for(int i = position+1; i <= furthermove; i++)
        for(int i = furthermove; i > position; --i)
        {
            if(Jump(nums,i,n))return true;
        }
        return false;
    }
};

但是该方法在74/75测试用例超出时间限制。

代码2(剪枝回溯/自顶向下)

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int n = nums.size();
        vector<int> flag;
        flag.resize(n,-1);
        flag[n-1] = 1;
        return Jump(nums,0,n,flag);
    }
    bool Jump(vector<int>& nums,int position,int n,vector<int>& flag)
    {
        if(flag[position] == 1){
            return true;
        }
        else if(flag[position] == 0) return false;
        int furthermove = min(n-1,position+nums[position]);
        //for(int i = position+1; i <= furthermove; i++)
        for(int i = furthermove; i > position; --i)
        {
            if(Jump(nums,i,n,flag))
            {
                flag[i] = 1;
                return true;
            }
        }
        flag[position] = 0;
        return false;
    }
};

但遗憾的是该方法在74/75测试用例仍然超出时间限制。

代码3(自底向上)

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int n = nums.size(),i,j;
        vector<int> flag;
        flag.resize(n,-1);
        flag[n-1] = 1;
        for(i = n-2; i > -1; --i)
        {
            int furthermove = min(n-1,i + nums[i]);
            for(j = i+1; j <= furthermove; ++j)
            {
                if(flag[j] == 1)
                {
                    flag[i] = 1;
                    break;
                }
            }
        }
        return flag[0] == 1;
        
    }
};

结果仍然不是很理想:

执行用时 :696 ms, 在所有 C++ 提交中击败了13.81%的用户
内存消耗 :15 MB, 在所有 C++ 提交中击败了5.04%的用户

代码4(贪心法)

用贪心来做,设置变量furthermove表示当前所能达到的最远的位置,那么状态转移方程为furthermove = max(furthermove, nums[i] + i),括号里的furthermove是上一步的最优解,因此是贪心。当到了某一个点 i>furthermove 的时候,说明已经走不到这一点了。

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int n = nums.size(),i,furthermove = nums[0];
        for(i = 1; i < n && i<= furthermove; ++i)
        {
           // if(i <= furthermove)
                furthermove = max(furthermove,i+nums[i]);
        }
        return furthermove>=n-1;       
    }
};

结果:

执行用时 :16 ms, 在所有 C++ 提交中击败了34.84%的用户
内存消耗 :14.7 MB, 在所有 C++ 提交中击败了5.04%的用户

45. 跳跃游戏 II

问题描述

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

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

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:

输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
说明:

假设你总是可以到达数组的最后一个位置。

问题分析

  • 1、如果某一个作为 起跳点 的格子可以跳跃的距离是 3,那么表示后面 3 个格子都可以作为 起跳点;可以对每一个能作为 起跳点 的格子都尝试跳一次,把 能跳到最远的距离furthermove 不断更新。
  • 2、首先从第0个元素开始,边界end是0,可以跳到的最远距离2,可以跳到1(i =1,furthermove就是4),也可以跳到2(i= 2,furthermove就是3),但是只有两个选择,贪心算法的要素就是获取当前阶段最优解;显然应该跳到1,此时边界end是4。遍历数组的时候,到了边界,我们就重新更新新的边界。
  • 3、如果furthermove的值大于等于len-1,那么就可以一直跳到最后,就成功了。

代码

class Solution {
public:
    int jump(vector<int>& nums) {
        int n = nums.size(),i,furthermove=0,end=0,step=0;
        if(n == 1)return 0;
        for(i = 0; i < n; i++)
        {
            furthermove = max(furthermove,nums[i]+i);
            if(furthermove>=n-1)return ++step;
            if(i == end)
            {
                end = furthermove;
                ++step;
            }
        }
        return step;
    }
};

问题分析:

执行用时 :12 ms, 在所有 C++ 提交中击败了73.08%的用户
内存消耗 :15.3 MB, 在所有 C++ 提交中击败了5.09%的用户
posted @ 2020-02-23 09:04  曲径通霄  阅读(280)  评论(0编辑  收藏  举报