返回

跳跃游戏系列挑战:一步步征服编程难关

Android

前言

LeetCode Jump Game系列是算法竞赛中颇具挑战性的一组题目,考察选手对动态规划的掌握程度和优化算法效率的能力。本篇文章将带领您逐一攻破七道题目,提供Kotlin代码和详细的解题思路,帮助您全面提升编程技能。

1. 跳跃游戏 I

题目
给定一个非负整数数组,表示可以从当前位置向前跳跃的距离。判断是否能够从数组的第一个位置跳到最后一个位置。

解题思路:
采用贪心算法,从第一个位置开始,每次跳跃至最远可达位置。若存在无法到达的位置,则返回false。

fun canJump(nums: IntArray): Boolean {
    var maxReach = 0
    for (i in 0 until nums.lastIndex) {
        if (i > maxReach) return false
        maxReach = maxOf(maxReach, i + nums[i])
    }
    return maxReach >= nums.lastIndex
}

2. 跳跃游戏 II

题目:
与跳跃游戏 I 类似,但这次需要计算从第一个位置跳到最后一个位置的最少跳跃次数。

解题思路:
采用动态规划,以子问题(从不同位置跳到最后一个位置的最小跳跃次数)的结果来构建整体解决方案。

fun jump(nums: IntArray): Int {
    val n = nums.size
    val dp = IntArray(n) { Int.MAX_VALUE }
    dp[n - 1] = 0
    for (i in n - 2 downTo 0) {
        for (j in i + 1..minOf(n - 1, i + nums[i])) {
            dp[i] = minOf(dp[i], dp[j] + 1)
        }
    }
    return if (dp[0] == Int.MAX_VALUE) -1 else dp[0]
}

3. 跳跃游戏 III

题目:
给定一个非负整数数组,表示可以从当前位置向前跳跃的距离,以及一个整数k,判断是否能够从数组的第一个位置跳到最后一个位置,且最多跳跃k次。

解题思路:
采用回溯法,从第一个位置开始尝试跳跃,若成功到达最后一个位置或跳跃次数达到k,则返回true。否则继续尝试下一个可跳跃位置。

fun canJump(nums: IntArray, k: Int): Boolean {
    return canJumpFromPosition(nums, 0, k)
}

private fun canJumpFromPosition(nums: IntArray, position: Int, k: Int): Boolean {
    if (position == nums.lastIndex) return true
    if (position > nums.lastIndex || k <= 0) return false
    for (i in 1..minOf(k, nums[position])) {
        if (canJumpFromPosition(nums, position + i, k - i)) {
            return true
        }
    }
    return false
}

4. 跳跃游戏 IV

题目描述:
与跳跃游戏 III 类似,但这次需要计算从第一个位置跳到最后一个位置的最少跳跃次数,且最多跳跃k次。

解题思路:
采用动态规划,以子问题(从不同位置跳到最后一个位置的最少跳跃次数)的结果来构建整体解决方案。

fun minJumps(nums: IntArray, k: Int): Int {
    val n = nums.size
    val dp = IntArray(n) { Int.MAX_VALUE }
    dp[n - 1] = 0
    for (i in n - 2 downTo 0) {
        for (j in i + 1..minOf(n - 1, i + nums[i])) {
            if (dp[j] != Int.MAX_VALUE) {
                dp[i] = minOf(dp[i], dp[j] + 1)
            }
        }
        if (i + k < n) {
            dp[i] = minOf(dp[i], dp[i + k] + 1)
        }
    }
    return if (dp[0] == Int.MAX_VALUE) -1 else dp[0]
}

5. 跳跃游戏 V

题目描述:
给定一个非负整数数组,表示可以从当前位置向前跳跃的距离。判断是否能够从数组的第一个位置跳到最后一个位置,且最多跳跃k次,且每个位置只能跳跃一次。

解题思路:
采用回溯法,从第一个位置开始尝试跳跃,若成功到达最后一个位置或跳跃次数达到k,则返回true。否则继续尝试下一个可跳跃位置。同时记录已经跳跃过的位置,避免重复跳跃。

fun canJump(nums: IntArray, k: Int): Boolean {
    val visited = BooleanArray(nums.size)
    return canJumpFromPosition(nums, 0, k, visited)
}

private fun canJumpFromPosition(nums: IntArray, position: Int, k: Int, visited: BooleanArray): Boolean {
    if (position == nums.lastIndex) return true
    if (position > nums.lastIndex || k <= 0) return false
    if (visited[position]) return false
    visited[position] = true
    for (i in 1..minOf(k, nums[position])) {
        if (canJumpFromPosition(nums, position + i, k - i, visited)) {
            return true
        }
    }
    return false
}

6. 跳跃游戏 VI

题目描述:
与跳跃游戏 V 类似,但这次需要计算从第一个位置跳到最后一个位置的最少跳跃次数,且最多跳跃k次,且每个位置只能跳跃一次。

解题思路:
采用动态规划,以子问题(从不同位置跳到最后一个位置的最少跳跃次数)的结果来构建整体解决方案。

fun minJumps(nums: IntArray, k: Int): Int {
    val n = nums.size
    val dp = IntArray(n) { Int.MAX_VALUE }
    dp[n - 1] = 0
    for (i in n - 2 downTo 0) {
        for (j in i + 1..minOf(n - 1, i + nums[i])) {
            if (dp[j] != Int.MAX_VALUE) {
                dp[i] = minOf(dp[i], dp[j] + 1)
            }
        }
        if (i + k < n) {
            dp[i] = minOf(dp[i], dp[i + k] + 1)
        }
    }
    return if (dp[0] == Int.MAX_VALUE) -1 else dp[0]
}

7. 跳跃游戏 VII

题目描述:
给定一个非负整数数组,表示可以从当前位置向前跳跃的距离。判断是否能够从数组的第一个位置跳到最后一个位置,且最多跳跃k次,且每个位置只能跳跃一次,且跳跃过程中不能跨越任何障碍。

解题思路:
采用回溯法,从第一个位置开始尝试跳跃,若成功到达最后一个位置或跳跃次数达到k,则返回true。否则继续尝试下一个可跳跃位置。同时记录已经跳跃过的位置,避免重复跳跃,并记录已经跨越过的障碍,避免跨越障碍。

fun canJump(nums: IntArray, k: Int): Boolean {
    val visited = BooleanArray(nums.size)
    val obstacles = BooleanArray(nums.size)
    return canJumpFromPosition(nums, 0, k, visited, obstacles)
}

private fun canJumpFromPosition(nums: IntArray, position: Int, k: Int, visited: BooleanArray, obstacles: BooleanArray): Boolean {
    if (position == nums.lastIndex) return true
    if (position > nums.lastIndex || k <= 0) return false
    if (visited[position]) return false
    if (obstacles[position]) return false
    visited[position] = true
    for (i in 1..minOf(k, nums[position])) {
        if (position + i