达到最后一个索引所需的最小步骤数

时间:2016-08-07 08:37:11

标签: arrays algorithm dynamic-programming

给定一个非负整数数组,您最初位于数组的第一个索引处。

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

您的目标是以最小跳跃次数到达最后一个索引。

例如: 给定数组A = [2,3,1,1,4]

到达最后一个索引的最小跳转次数为2.(从索引0跳转1步到1,然后从最后一个索引跳3步。)

我从左到右构建了一个dp []数组,使得dp [i]表示从arr [0]到达arr [i]所需的最小跳跃次数。最后,我们返回dp [n-1]。

我的代码最糟糕的时间复杂度是O(n ^ 2)。

这可以在更好的时间复杂度下完成。

**此问题是从leetcode复制的。

6 个答案:

答案 0 :(得分:1)

您可以使用范围最小的段树来解决此问题。段树是一种数据结构,它使您可以维护值的数组,也可以查询该数组的子段的聚合操作。可在此处找到更多信息:https://cses.fi/book/book.pdf(第9.3节)

您将在分区树中存储值d[i],如果从索引d[i]开始,i是到达最后一个索引所需的最少步骤数。显然,d[n-1] = 0。一般来说:

d[i] = 1 + min(d[i+1], ..., d[min(n-1, i+a[i])])

因此您可以通过向后计算它们来找到d中的所有值,并在每个步骤之后更新分段树。最终的解决方案是d[0]。由于对段树的更新和查询都可以在O(log n)中进行,因此整个算法都可以在O(n log n)中进行工作。

答案 1 :(得分:0)

我认为,你可以用这些技术提升计算动力:
你花费O(N)来计算当前的d [i]。但是你可以用d [j]保持一组, 其中j = 0..i - 1.现在你需要使用二进制搜索来找到:

这样的d [j],在所有(0..i-1)中最小,从j位置i-pos是可到达的。


它将是 O(n * logn)解决方案

答案 2 :(得分:0)

这是动态编程中的一个简单练习。正如你已经标记过它,我想知道你为什么不试图应用它。

V[k]为从k位置到列表a = (a[0], a[1], ...., a[n-1])末尾的最小步数。

然后显然是V[n-1]=0。现在循环向后:

for(int k=n-2;k>=0;--k)
{
    int minStep = n + 1;
    for(int j=k+1;j<=std::min(n-1,k+a[k]);++j)
    {
        minStep = std::min(minStep, V[j])
    }
    V[k]= minStep + 1;
}

Demo in C++

循环后,O(a[0]+a[1]+...+a[n-1])时间V[0]包含到达列表末尾的最小步数。

为了找到列表中的方法,您可以贪婪地选择操作。也就是说,从位置k开始,您总是会转到允许的位置l,其中V[l]是最小的。

(请注意,我在此处假设列表的条目,而不是非负的条目。可能很容易从问题中删除可能的零,因为去那里绝对不是最佳的。)

答案 3 :(得分:0)

int jump(vector<int>& a) {
    int i,j,k,n,jumps,ladder,stairs;
    n = a.size();
    if(n==0 || n==1)return 0;
    jumps = 1, ladder = stairs = a[0];
    for(i = 1; i<n; i++){
        if(i + a[i] > ladder)
        ladder = i+a[i];
        stairs --;
        if(stairs + i >= n-1)
        return jumps;
        if(stairs == 0){
            jumps++;
            stairs = ladder - i;
        }
    }
    return jumps;
}

答案 4 :(得分:0)

https://leetcode.com/problems/jump-game-ii

class Solution {
    public int jump(int[] nums) {
        int n = nums.length;
        if(n < 2){
            return 0;
        }
        int ans = 1;

        int rightBoundaryCovered = nums[0];
        
        for(int i=1;i<n;i++){
            if(rightBoundaryCovered >= n-1){
                return ans;
            }
            int currMax = i+ nums[i];
            while(rightBoundaryCovered>=i){
                currMax = Math.max(currMax, i+nums[i]);
                i++;
            }
            //missed this decrement statement and faced multiple WA's
            i--;
            ans++;
            if(currMax>rightBoundaryCovered){
                rightBoundaryCovered = currMax;    
            }
        }
        return ans;
    }
}

答案 5 :(得分:-1)

Java解决方案(来自编程面试要素):

public boolean canJump(int[] nums) {
    int maximumReach = 0;

    for(int i = 0; i < nums.length; i++) {

        // Return false if you jump more. 
        if(i > maximumReach) { return false; }

        // Logic is we need to keep checking every index the 
        // farthest we can travel
        // Update the maxReach accordingly.
        maximumReach = Math.max(i + nums[i], maximumReach);
    }

    return true;
}