跳跃游戏(贪心->动态规划)

简介: 跳跃游戏(贪心->动态规划)

1.跳跃游戏(55-中)



题目描述:给定一个非负整数数组 nums你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标。


示例


输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。


思路:本题只是问是否能达到最后一个下标。最优解贪心:计算每个位置最大的覆盖范围,看能否覆盖最后一个位置,维护一个变量记录最大覆盖位置,遍历数组即可。


注意:遍历范围为最大覆盖范围(不断更新),而不是数组的每个位置!!!


代码

public boolean canJump(int[] nums) {
    int n = nums.length;
    int cover = 0;
    for (int i = 0; i <= cover; i++) {
        cover = Math.max(cover, i + nums[i]);
        if (cover >= n - 1) {
            return true;
        }
    }
    return false;
}


2.跳跃游戏II(45-中)



题目描述:给定一个非负整数数组,你最初位于数组的第一个位置。


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


你的目标是使用最少的跳跃次数到达数组的最后一个位置。


假设你总是可以到达数组的最后一个位置。


示例

输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。


思路:与上一题不同,本题目标是:覆盖最后一个位置的最少跳跃次数。


贪心策略下一次跳跃在上次能跳到的范围(end)内选择一个能跳的最远的位置作为下次的起跳点 !


注意:在遍历数组时,我们不访问最后一个元素,这是因为在访问最后一个元素之前,我们的边界一定大于等于最后一个位置,否则就无法跳到最后一个位置了。如果访问最后一个元素,在边界正好为最后一个位置的情况下,我们会增加一次「不必要的跳跃次数」,因此我们不必访问最后一个元素。


代码

public int jump(int[] nums) {
    int n = nums.length;
    int end = 0, maxCover = 0;
    int cnt = 0;
    for (int i = 0; i < n - 1; i++) {
        maxCover = Math.max(maxCover, i + nums[i]);
        // 到达上一次最大跳跃的右边界
        if (i == end) {
            end = maxCover;
            cnt++;
        }
    }
    return cnt;
}


3.跳跃游戏III(1306-中)



题目描述:这里有一个非负整数数组 arr,你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时,你可以跳到 i + arr[i] 或者 i - arr[i]位置。


请你判断自己是否能够跳到对应元素值为 0 的 任一 下标处。注意,不管是什么情况下,你都无法跳到数组之外。


示例

输入:arr = [4,2,3,0,3,1,2], start = 5
输出:true
解释:
到达值为 0 的下标 3 有以下可能方案: 
下标 5 -> 下标 4 -> 下标 1 -> 下标 3 
下标 5 -> 下标 6 -> 下标 4 -> 下标 1 -> 下标 3


思路:使用递归进行求解,dfs。


  • 定义一个访问数组,标记该位置是否遍历过。如果当前节点没访问过,标记为true。如果下一次再访问该点说明进入了循环,直接返回false;
  • 递归终止条件:索引越界,返回false;跳到目标值,返回true
  • 递归基本逻辑如题意所述。


代码

public boolean canReach(int[] arr, int start) {
    int n = arr.length;
    boolean[] visit = new boolean[n];
    return dfs(visit, arr, start);
}
public boolean dfs(boolean[] visit, int[] arr, int start) {
    if (start < 0 || start >= arr.length) {
        return false;
    }
    if (arr[start] == 0) {
        return true;
    }
    // 排除重复循环问题
    if (visit[start]) {
        return false;
    } else {
        visit[start] = true;
    }
    return dfs(visit, arr, start + arr[start]) || dfs(visit, arr, start - arr[start]);
}


4.跳跃游戏IV(1696-中)



题目描述:给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。


一开始你在下标 0 处。每一步,你最多可以往前跳 k 步,但你不能跳出数组的边界。也就是说,你可以从下标 i 跳到 [i + 1, min(n - 1, i + k)] 包含 两个端点的任意位置。

你的目标是到达数组最后一个位置(下标为 n - 1 ),你的 得分 为经过的所有数字之和。


请你返回你能得到的 最大得分 。


示例

输入:nums = [1,-1,-2,4,-7,3], k = 2
输出:7
解释:你可以选择子序列 [1,-1,4,3] ,和为 7 。


思路:很显然本题考查动态规划,我们用dp[i]表示以第i个元素结尾的最大得分,那么很明显,每次我们只需要找到dp[i - 1]、dp[i - 2]、...、dp[i - k]的最大值,然后加上当前元素的大小,就可以得到dp[i]。但是超时。


我们每次都要与前面的值进行比较,比较消耗性能,其实我们只需要之前的最大值。这个过程可以使用优先队列(T239滑动窗口的最大值)进行优化:双端队列queue保存滑动窗口中的最大值索引的同时,把最大值索引的【候补】也装进来。


  • 新的索引i对应的得分dp[i - 1]大于队尾对应数值的元素dp[tail],循环弹出队内元素,否则直接入队。
  • 如果队列头是否已经失效,失效直接弹出队头
  • 记录这时的最大值(最大得分)


代码

// 动态规划:超时 
public int maxResult(int[] nums, int k) {
    int n = nums.length;
    int[] dp = new int[n];
    Arrays.fill(dp, Integer.MIN_VALUE);
    // dp[i]:以i结尾的最大得分
    dp[0] = nums[0];
    for (int i = 1; i < n; ++i) {
        for (int j = Math.max(0, i - k); j < i; ++j) {
            dp[i] = Math.max(dp[i], dp[j]);
        }
        dp[i] += nums[i];
    }
    return dp[n - 1];
}
// 优先队列优化
public int maxResult(int[] nums, int k) {
    int n = nums.length;
    Deque<Integer> queue = new LinkedList<>();
    int[] dp = new int[n];
    dp[0] = nums[0];
    for (int i = 1; i < n; ++i) {
        while (!queue.isEmpty() && dp[i - 1] >= dp[queue.peekLast()]) {
            queue.pollLast();
        } 
        queue.addLast(i - 1);
        // 索引不符合步数范围
        if (queue.peekFirst() < i - k) {
            queue.pollFirst();
        }
        dp[i] = dp[queue.peekFirst()] + nums[i];
    }
    return dp[n - 1];
}


相关文章
|
7月前
|
算法 测试技术 C#
【动态规划】1223. 掷骰子模拟
【动态规划】1223. 掷骰子模拟
|
算法
代码随想录 Day26 贪心算法01 中 LeetCode T376 摆动序列
代码随想录 Day26 贪心算法01 中 LeetCode T376 摆动序列
56 1
代码随想录Day27贪心02 下 LeetCodeT45 跳跃游戏II
代码随想录Day27贪心02 下 LeetCodeT45 跳跃游戏II
35 0
代码随想录 Day27 贪心02中 LeetCode T55跳跃游戏
代码随想录 Day27 贪心02中 LeetCode T55跳跃游戏
38 0
【动态规划刷题 5】 最小路径和&&地下城游戏
【动态规划刷题 5】 最小路径和&&地下城游戏
|
7月前
|
机器学习/深度学习 算法 测试技术
【动态规划】【C++算法】1563 石子游戏 V
【动态规划】【C++算法】1563 石子游戏 V
|
7月前
|
算法 测试技术 vr&ar
【动态规划】【C++算法】1340. 跳跃游戏 V
【动态规划】【C++算法】1340. 跳跃游戏 V
|
7月前
|
算法 测试技术 C++
【状态压缩】【动态规划】【C++算法】691贴纸拼词
【状态压缩】【动态规划】【C++算法】691贴纸拼词
【动态规划入门】动态规划思路五部曲_斐波那契_爬楼梯_最小花费爬楼梯
【动态规划入门】动态规划思路五部曲_斐波那契_爬楼梯_最小花费爬楼梯
71 0