• [array] leetcode-55. Jump Game


    leetcode-55. Jump Game - Medium

    descrition

    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.

    For example:
    A = [2,3,1,1,4], return true.
    
    A = [3,2,1,0,4], return false.
    

    解析

    这是一道动态规划的题,官网有很详细的解答leetcode-solution

    动态规划算法的设计步骤:

    1. 递归回溯求解(自顶向下)
    2. 使用记忆回溯法求解 (自顶向下的动态规划)
    3. 去掉递归,使用自底向上的动态规划
    4. 进一步使用技巧优化自底向上的动态规划

    根据以上步骤得到以下的解决方法。代码只给出最有解,其他可参考官网的 solution。

    方法 1 :

    回溯法,recursive(vector& nums, int icur)

    • 如果 icur == nums.size() - 1,到达最后一个位置,递归的出口,表示可以到达最后,返回 true。
    • 递归:i, 1+icur <= i <= min(icur+nums[icur], n-1)

    即对每一种情况都递归检查一遍。

    时间复杂度-O(2^n); 空间复杂度-O(n),递归需要额外的空间。

    方法 2

    实际上就是在方法 1 的基础上系上一个 memory 数组,表示当前访问下标 icur 的状态,这样可以避免重复计算。

    递归函数可以定义为:recursive(vector& nums, int icur, vector& memory)

    时间复杂度-O(n^2),n 个元素,没个元素最多有 n 种可能;空间复杂度-O(n),递归需要额外空间。

    方法 3

    方法 1 和 方法 2 在对每一步进行试探时从近到远。还可以考虑从远到近进行检查,即每次都从可能到大的最远处进行检查。这种方法的时间复杂度并没有优化,但是从实践的角度可以进行一定的优化。

    方法 4

    动态规划方法。假设数组 dp[i] 表示位置 i 的状态:UNKONW, GOOD, BAD,分别表示未知,可达,不可达状态。

    那么对于任意位置 i ,检查所有的 j = [i+1, min(i+nums[i], n-1)] 的状态,只要有一个 dp[j] = GOOD,则说明 dp[i] 也是 GOOD。边界条件 dp[n-1] = GOOD。

    最后我们只需要检查 dp[0] 是否等于 GOOD 即可。

    方法 5

    对动态规划的进一步优化。核心思想贪心。我们可以从两个角度来看。最优解,时间复杂度-O(n),空间复杂度-O(1)。

    1. 从右往左遍历

    令 lastPos 表示当前可以到达的最靠近左边的位置。从右边往左边遍历:for(int i=n-1; i>=0, i--)

    对任意 i,最远可到达的位置为 farthest = i + nums[i],如果 farthest >= lastPos,则说明从当前位置可以到达最尾的位置,此时更新 lastPos = i 。

    最后检查 lastPos 是否为 0 即可。

    2. 从左往右遍历

    令 farthest 表示能到达的最远距离,我们从左往右遍历:for(int i=0; i<n; i++)。直观来说就是我们每次都往前移动一步,如果 i > farthest 则说明不可能到达最尾节点,直接返回 false。循环中每次都更新 farthest = max(i+nums[i], farthest)。

    如果循环能顺利结束,则说明可以到达最尾节点。

    code

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    class Solution{
    public:
    	bool canJump(vector<int>& nums){
    		//return canJumpRight2Left(nums);
    		return canJumpLeft2Right(nums);
    	}	
    
    	bool canJumpRight2Left(vector<int>& nums){
    		int lastPos = nums.size() - 1;
    		for(int i=nums.size()-1; i>=0; i--){
    			if((i + nums[i]) >= lastPos){
    				lastPos = i;
    			}
    		}
    		return lastPos == 0;
    	}
    
    	bool canJumpLeft2Right(vector<int>& nums){
    		int farthest = 0;
    		for(int i=0; i<nums.size(); i++){
    			if( i > farthest)
    				return false;
    			farthest = max(i + nums[i], farthest);
    		}
    
    		return true;
    	}
    };
    
    int main()
    {
    	return 0;
    }
    
  • 相关阅读:
    双日历时间段选择控件—daterangepicker(汉化版)
    vue elementui table表格展开行每次只展开一行
    vue-pdf PDF文件预览
    async await
    vuex发送axios请求
    jq调用浏览器下载文件 window.open()
    禁止页面右键、选择、F12操作
    vue 点击一条消息跳转相应的页面且对应相应的大模块和办理状态
    vue-infinite-scroll 滚动加载下一页
    填写流程当前登录人可以填写除自己可填项外还可看到他前面经办人相关填写的内容,且经办人后面的不可见
  • 原文地址:https://www.cnblogs.com/fanling999/p/7892450.html
Copyright © 2020-2023  润新知