跳跃游戏-----原博主的分析很精彩

跳跃游戏

给出一个非负整数数组,你最初定位在数组的第一个位置。    
数组中的每个元素代表你在那个位置可以跳跃的最大长度。     
判断你是否能到达数组的最后一个位置。

样例 
A = [2,3,1,1,4],返回 true.

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

注意 
这个问题有两个方法,一个是贪心和 动态规划。

贪心方法时间复杂度为O(N)。

动态规划方法的时间复杂度为为O(n^2)。

我们手动设置小型数据集,使大家阔以通过测试的两种方式。这仅仅是为了让大家学会如何使用动态规划的方式解决此问题。如果您用动态规划的方式完成它,你可以尝试贪心法,以使其再次通过一次。

Solution:

方法一.动态规划求解

首先来看一下如何使用动态规划求解该问题。使用动态规划求解问题,首先需要找到问题的状态和状态转化方程

假设问题的状态,假设位置 i(0iA.length) 能够跳跃的最大长度为dp[i]。 
对于数组A = [2,3,1,1,4], 则有: 
i = 0, dp[0] = A[0] + 0 = 2 
i = 1, if dp[i-1] = dp[0]  i then dp[1] =  max{A[1]+1,dp[0]}=4  else dp[1] = 0 
i = 2, if dp[i-1] = dp[1]   i then dp[2] =  max{A[2]+2,dp[1]}  = 4 else dp[2] = 0

基于上面的分析,其状态转换方程为: 
dp[i]={max{A[i]+i,dp[i1]},0,if dp[i1]iotherwise  
注意:需要判断能否到达第 i 个位置

public static boolean canJumpDP(int [] A) {
    if (A.length == 1) return true;

    int [] dp = new int[A.length];
    dp[0] = A[0];
    for (int i = 1; i < dp.length; ++i) {
        if (dp[i-1] >= i)
            dp[i] = max(A[i]+i, dp[i-1]);
        else
            dp[i] = 0;
    }
    return dp[dp.length-1] >= A.length-1;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
方法二:贪心算法

如果对上面的代码认真分析以下的话,是没有必要创建dp数组的,可以使用一个变量来进行替换。及 
currMaxStep 用来记录从0到第 i 个位置中所能跳到最远的距离。

public boolean canJump(int[] A) {
    int currMaxStep = A[0];  //当前能够跳跃的最大步数
    int step = 0;
    for (int i = 1; i < A.length; ++i) {
        if (i > currMaxStep) return false;
        currMaxStep = max(i+A[i], currMaxStep);
        if (currMaxStep >= A.length-1) return true;
    }
    return currMaxStep >= A.length-1;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

跳跃游戏 II

给出一个非负整数数组,你最初定位在数组的第一个位置。 
数组中的每个元素代表你在那个位置可以跳跃的最大长度。    
你的目标是使用最少的跳跃次数到达数组的最后一个位置。

样例 
给出数组A = [2,3,1,1,4],最少到达数组最后一个位置的跳跃次数是2(从数组下标0跳一步到数组下标1,然后跳3步到数组的最后一个位置,一共跳跃2次)

方法一:动态规划求解

首先,假设问题的状态:“假设跳到数组第 i(0iA.length1) 个位置,需要使用的最少次数为 dp[i] ”。 
对于数组 A = [2,3,1,1,4], 有: 
i=0,dp[0]=0 (开始就在第一个位置) 
i=1,{2},dp[1]=1  
i=2,{2,3},dp[2]=min(dp[0]+1,dp[1]+1)=1  
i=3,{2,3,1},dp[3]=min(dp[1]+1,dp[2]+1)=2

因此,转态转换方程为: 
dp[i]=min{dp[j]+1}  and  j+A[j]i , for  0ji1

Talk is cheap, show me the code !

/**
* @param A: A list of lists of integers
* @return: An integer
*/
public int jump(int[] A) {
    // write your code here
    int [] dp = new int[A.length];
    Arrays.fill(dp, A.length);  //初始化
    dp[0] = 0;
    for (int i = 1; i < dp.length; ++i) {
        for (int j = 0; j < i; ++j) {
            if (A[j] + j >= i)
                dp[i] = min(dp[i], dp[j]+1);
        }
    }
    return dp[dp.length-1];
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这是我用python2实现的,逻辑应该没问题但是过不了计蒜客的oj

# -*-coding:utf-8 -*-
def dp(arr,num):
    count= [999]*num #保存跳跃次数
    count[0] = 0
    # print(len(dp))
    if num <= 1:
        return 0
    for i in xrange(1,len(arr)):
        for j in xrange(i):
            if arr[j] +j >= i:
                count[i] = min(count[i],count[j]+1)
    return count[num-1]

num = int(raw_input())
arr = map(int,raw_input().split(' '))
print(dp(arr,num))


时间复杂度 O(n2) , 空间复杂度 O(n)

方法二:贪心算法

如果这代码放在LeetCode上运行是会超时的。与Jump Code I 一样这题也是可以使用贪心算法来进行求解的。。 该代码和思路来自 LeetCode discuss

其主要是基于贪心的思想。假设当前(位置i)能够跳最远的范围为[curBegin, curEnd], curFarther是在[curBegin, curEnd]范围内能够跳的最远距离(位置)。一旦当前位置(i)到了curEnd, 那么就会触发一次跳跃,并且设置curEnd等于curFarthest。按照上面的步骤,直到跳跃到最后一个位置为止。其代码为:

public int jump(int [] A) {
    int jumps = 0;  //当前已经跳跃的步数
    int curEnd = 0; //当前位置能够跳跃的最远距离
    int curFarthest = 0; //在[0, curEnd]范围能的位置,能够跳跃的最远距离

    for (int i = 0; i < A.length-1; ++i) {
        curFarthest = max(curFarthest, i+A[i]);
        if (i == curEnd) {
            jumps++;
            curEnd = curFarthest;
            if (curFarthest >= A.length-1) return jumps;
        }
    }
    return curFarthest >= A.length-1 ? jumps : -1;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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

方法三: BFS

LeetCode Discuss 上提出了BFS方法

其实可以把这个为题转换为BFS问题(注:如果认真思考一下的话, 可以看成单源最短路径问题)。及第 i 层所有的节点都能够在 i1 步下到达。例如 2 3 1 1 4 ==> 2 || 3 1 || 1 4 ||

代码为:

public int jumpBFS(int [] A) {
    if (A.length < 2) return 0;

    int level = 0, i = 0;
    int currentMax=0; //当前层能够到达的最远位置
    int nextMax=0;  //下一层能够到达的最大位置
    while (currentMax - i + 1 > 0) { 
        level++;
         for (; i <= currentMax; ++i) {
             nextMax = max(nextMax, A[i]+i);
             if (nextMax >= A.length -1) return level;
         }
        currentMax = nextMax;
    }
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值