算法 LC 動態(tài)規(guī)劃-跳躍游戲

題目描述

LC 題 - 55
給定一個非負整數(shù)數(shù)組 nums ,你最初位于數(shù)組的 第一個下標 。

數(shù)組中的每個元素代表你在該位置可以跳躍的最大長度。

判斷你是否能夠到達最后一個下標。

示例 1:

輸入:nums = [2,3,1,1,4]
輸出:true
解釋:可以先跳 1 步,從下標 0 到達下標 1, 然后再從下標 1 跳 3 步到達最后一個下標。

示例 2:
輸入:nums = [3,2,1,0,4]
輸出:false
解釋:無論怎樣,總會到達下標為 3 的位置。但該下標的最大跳躍長度是 0 , 所以永遠不可能到達最后一個下標。

題解

思路1:動態(tài)規(guī)劃

核心點在于數(shù)組中元素為0時能否跳過

令dp[i] 表示i此時可以跳的最大步長,當遍歷到某個i,dp[i]<=0時,說明無法再向前,則返回false

dp[i]的動態(tài)轉(zhuǎn)移方程:dp[i] = max(nums[i],dp[i-1]-1),即當前位置i的步長和上一個位置i-1的最大步長-1兩者的最大值
邊界條件dp[0] = nums[0]

// OC
+ (BOOL)canJump1:(NSArray *)nums {
    int n = (int)nums.count;
    if (n == 0) {
        return NO;
    }
    int dp[n];
    dp[0] = [nums[0] intValue];
    if (dp[0] <= 0) {
        return n==1;
    }
    
    for (int i=1; i<n; i++) {
        dp[i] = MAX([nums[i] intValue], dp[i-1]-1);
        if (dp[i] <= 0 && i<n-1) {
            return NO;
        }
    }
    return YES;
    
}
// Swift
    static public func canJump1(_ nums:[Int]) -> Bool {
        let n = nums.count
        if n == 0 {return false}
        var dp = Array(repeating: 0, count: n)
        dp[0] = nums[0]
        if dp[0] <= 0 {
            return n==1
        }
        
        for i in 1..<n {
            dp[i] = max(dp[i-1]-1, nums[i])
            if dp[i] <= 0 && i<n-1 {
                return false
            }
        }
        return true
    }

思路2:貪心

對于任意位置i,它所能到達的最遠位置為maxRight = i + nums[i],也就是說 對于每一個可以到達的位置x,它使得 x+1,x+2,?,x+nums[x] 這些連續(xù)的位置都可以到達。

我們依次遍歷數(shù)組中的每一個位置,并實時維護能到達的最遠位置maxRight,如果當前遍歷的位置i<=maxRight(也就是說當前位置i可以到達),那么我們用max(maxRight,i+nums[i])來更新最遠到達位置maxRight,在遍歷過程中,如果maxRight>=y(y為指定位置),則表示y位置可以到達,返回true。如果直到遍歷結(jié)束都沒有maxRight>=y,則返回false

// OC
+ (BOOL)canJump2:(NSArray *)nums {
    int n = (int)nums.count;
    if (n == 0) {
        return NO;
    }
    int maxRight = 0;
    for (int i=0; i<n; i++) {
        if (i<=maxRight) {
            maxRight = MAX([nums[i] intValue]+i, maxRight);
            if (maxRight >= n-1) {
                return YES;
            }
        }
    }
    return NO;
}
// Swift
    static public func canJump2(_ nums:[Int]) -> Bool {
        let n = nums.count
        if n == 0 {return false}
        var maxRight = 0
        for i in 0..<n {
            if i <= maxRight {
                maxRight = max(maxRight, i+nums[i])
                if maxRight >= n-1 {
                    return true
                }
            }
        }
        return false
    }

思路3: 逆向遍歷

如果位置i可以到達終點k點,那么逆序時k點也是可以回到i點的

令初始終點end=n-1,逆序遍歷i,i從n-2到0,如果nums[i]+i>=end,則更新終點end=i,遍歷結(jié)束判斷終點是否為0,為0則返回true ,否則false

假設(shè)位置i,j(i<j)都可以到達終點end=k(i<j<k,nums[i]+i>=k,nums[j]+j>=k),逆序遍歷時,先遍歷到j(luò),由于nums[j]+j>=k,即從j位置可以到達k點,更新終點end=j,再遍歷到i時,由于nums[i]+i>=k>j,即從i位置可以到達j,更新終點end=i。也就是說如果i點可以到達終點end,那么從終點也可以回到初始位置i

// OC
+ (BOOL)canJump3:(NSArray *)nums {
    int n = (int)nums.count;
    if (n == 0) {
        return NO;
    }
    int end = n-1;
    for (int i=end-1; i>=0; i--) {
        if ([nums[i] intValue] + i >= end) {
            end = i;
        }
    }
    return end == 0;
}
// Swift
    static public func canJump3(_ nums:[Int]) -> Bool {
        let n = nums.count
        if n == 0 {return false}
        var end = n-1
        var i = n-2
        while i>=0 {
            if nums[i] + i >= end {
                // 更新終點
                end = i
            }
            i -= 1
        }
        return end==0
    }

參考:https://leetcode-cn.com/leetbook/read/top-interview-questions-medium/xvb8zs/

https://leetcode-cn.com/problems/jump-game/solution/tiao-yue-you-xi-by-leetcode-solution/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容