zoukankan      html  css  js  c++  java
  • 动态规划-贪心-BFS-跳跃游戏

    2020-05-04 17:21:37

    55. 跳跃游戏

    问题描述:

    给定一个非负整数数组,你最初位于数组的第一个位置。

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

    判断你是否能够到达最后一个位置。

    示例 1:

    输入: [2,3,1,1,4]
    输出: true
    解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
    示例 2:

    输入: [3,2,1,0,4]
    输出: false
    解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

    问题求解:

    维护rightMost即可。

    时间复杂度:O(n)

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

     

    1306. 跳跃游戏 III

    问题描述:

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

    请你判断自己是否能够跳到对应元素值为 0 的 任意 下标处。

    注意,不管是什么情况下,你都无法跳到数组之外。

    示例 1:

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

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

    输入:arr = [3,0,2,1,2], start = 2
    输出:false
    解释:无法到达值为 0 的下标 1 处。
    提示:

    1 <= arr.length <= 5 * 10^4
    0 <= arr[i] < arr.length
    0 <= start < arr.length

    问题求解:

    BFS经典问题。

    时间复杂度:O(n)

        public boolean canReach(int[] arr, int start) {
            int n = arr.length;
            int[] used = new int[n];
            Queue<Integer> q = new LinkedList<>();
            q.add(start);
            used[start] = 1;
            while (!q.isEmpty()) {
                int curr = q.poll();
                int l = curr - arr[curr];
                int r = curr + arr[curr];
                if (l >= 0 && arr[l] == 0) return true;
                if (r < n && arr[r] == 0) return true;
                if (l >= 0 && used[l] == 0) {
                    q.add(l);
                    used[l] = 1;
                }
                if (r < n && used[r] == 0) {
                    q.add(r);
                    used[r] = 1;
                }
            }
            return false;
        }
    

     

    45. 跳跃游戏 II

    问题描述:

    给定一个非负整数数组,你最初位于数组的第一个位置。

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

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

    示例:

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

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

    问题求解:

    解法一:DP

    时间复杂度:O(n ^ 2)

        public int jump(int[] nums) {
            int n = nums.length;
            int[] dp = new int[n];
            dp[n - 1] = 0;
            for (int i = n - 2; i >= 0; i--) {
                dp[i] = Integer.MAX_VALUE / 2;
                if (nums[i] + i >= n - 1) dp[i] = 1;
                else {
                    int step = dp[nums[i] + i];
                    for (int j = nums[i] + i; j > i; j--) step = Math.min(step, dp[j]);
                    dp[i] = step + 1;
                }
            }
            return dp[0];
        }

    解法二:BFS 

    数组上的BFS。

    时间复杂度:O(n)

        public int jump(int[] nums) {
            int n = nums.length;
            int layer = 0;
            int nlayer = 0;
            int step = 0;
            for (int i = 0; i < n - 1; i++) {
                if (i <= layer) {
                    nlayer = Math.max(nlayer, i + nums[i]);
                    if (nlayer >= n - 1) return step + 1;
                }
                else {
                    layer = nlayer;
                    nlayer = 0;
                    step++;
                    i--;
                }
            }
            return 0;
        }
    

      

    1345. 跳跃游戏 IV

    问题描述:

    给你一个整数数组 arr ,你一开始在数组的第一个元素处(下标为 0)。

    每一步,你可以从下标 i 跳到下标:

    i + 1 满足:i + 1 < arr.length
    i - 1 满足:i - 1 >= 0
    j 满足:arr[i] == arr[j] 且 i != j
    请你返回到达数组最后一个元素的下标处所需的 最少操作次数 。

    注意:任何时候你都不能跳到数组外面。

    示例 1:

    输入:arr = [100,-23,-23,404,100,23,23,23,3,404]
    输出:3
    解释:那你需要跳跃 3 次,下标依次为 0 --> 4 --> 3 --> 9 。下标 9 为数组的最后一个元素的下标。
    示例 2:

    输入:arr = [7]
    输出:0
    解释:一开始就在最后一个元素处,所以你不需要跳跃。
    示例 3:

    输入:arr = [7,6,9,6,9,6,9,7]
    输出:1
    解释:你可以直接从下标 0 处跳到下标 7 处,也就是数组的最后一个元素处。
    示例 4:

    输入:arr = [6,1,9]
    输出:2
    示例 5:

    输入:arr = [11,22,7,7,7,7,7,7,7,22,13]
    输出:3

    提示:

    1 <= arr.length <= 5 * 10^4
    -10^8 <= arr[i] <= 10^8

    问题求解:

    带传送门的BFS,需要注意的要删去传送门。

    时间复杂度:O(n)

        public int minJumps(int[] arr) {
            if (arr.length <= 1) return 0;
            int n = arr.length;
            Map<Integer, List<Integer>> map = new HashMap<>();
            for (int i = 0; i < n; i++) {
                if (!map.containsKey(arr[i])) map.put(arr[i], new ArrayList<>());
                map.get(arr[i]).add(i);
            }
            Queue<Integer> q = new LinkedList<>();
            int[] used = new int[n];
            Set<Integer> set = new HashSet<>();
            q.add(0);
            used[0] = 1;
            int step = 0;
            while (!q.isEmpty()) {
                int size = q.size();
                for (int k = 0; k < size; k++) {
                    int curr = q.poll();
                    if (curr - 1 >= 0 && used[curr - 1] == 0) {
                        q.add(curr - 1);
                        used[curr - 1] = 1;
                    }
                    if (curr + 1 < n && used[curr + 1] == 0) {
                        if (curr + 1 == n - 1) return step + 1;
                        q.add(curr + 1);
                        used[curr + 1] = 1;
                    }
                    if (!set.contains(arr[curr])) {
                        set.add(arr[curr]);
                        for (int next : map.get(arr[curr])) {
                            if (next == n - 1) return step + 1;
                            if (used[next] == 1) continue;
                            q.add(next);
                            used[next] = 1;
                        }
                    }
                }
                step += 1;
            }
            return -1;
        }
    

      

    1340. 跳跃游戏 V

    问题描述:

    给你一个整数数组 arr 和一个整数 d 。每一步你可以从下标 i 跳到:

    i + x ,其中 i + x < arr.length 且 0 < x <= d 。
    i - x ,其中 i - x >= 0 且 0 < x <= d 。
    除此以外,你从下标 i 跳到下标 j 需要满足:arr[i] > arr[j] 且 arr[i] > arr[k] ,其中下标 k 是所有 i 到 j 之间的数字(更正式的,min(i, j) < k < max(i, j))。

    你可以选择数组的任意下标开始跳跃。请你返回你 最多 可以访问多少个下标。

    请注意,任何时刻你都不能跳到数组的外面。

    示例 1:

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/jump-game-v
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    输入:arr = [6,4,14,6,8,13,9,7,10,6,12], d = 2
    输出:4
    解释:你可以从下标 10 出发,然后如上图依次经过 10 --> 8 --> 6 --> 7 。
    注意,如果你从下标 6 开始,你只能跳到下标 7 处。你不能跳到下标 5 处因为 13 > 9 。你也不能跳到下标 4 处,因为下标 5 在下标 4 和 6 之间且 13 > 9 。
    类似的,你不能从下标 3 处跳到下标 2 或者下标 1 处。
    示例 2:

    输入:arr = [3,3,3,3,3], d = 3
    输出:1
    解释:你可以从任意下标处开始且你永远无法跳到任何其他坐标。
    示例 3:

    输入:arr = [7,6,5,4,3,2,1], d = 1
    输出:7
    解释:从下标 0 处开始,你可以按照数值从大到小,访问所有的下标。
    示例 4:

    输入:arr = [7,1,7,1,7,1], d = 2
    输出:2
    示例 5:

    输入:arr = [66], d = 1
    输出:1

    提示:

    1 <= arr.length <= 1000
    1 <= arr[i] <= 10^5
    1 <= d <= arr.length

    问题求解:

    解法一:递归DP

    最容易想到的解法就是Top-down DP。

    时间复杂度:O(nd)

        public int maxJumps(int[] arr, int d) {
            int n = arr.length;
            int[] dp = new int[n];
            for (int i = 0; i < n; i++) {
                if (dp[i] == 0) dp[i] = dfs(arr, d, i, dp);
            }
            int res = dp[0];
            for (int i = 0; i < n; i++) res = Math.max(res, dp[i]);
            return res;
        }
        
        private int dfs(int[] arr, int d, int idx, int[] dp) {
            if (dp[idx] != 0) return dp[idx];
            dp[idx] = 1;
            for (int i = idx - 1; i >= Math.max(0, idx - d); i--) {
                if (arr[i] >= arr[idx]) break;
                dp[idx] = Math.max(dp[idx], dfs(arr, d, i, dp) + 1);
            }
            for (int i = idx + 1; i <= Math.min(arr.length - 1, idx + d); i++) {
                if (arr[i] >= arr[idx]) break;
                dp[idx] = Math.max(dp[idx], dfs(arr, d, i, dp) + 1);
            }
            return dp[idx];
        }
    

     

    解法二:递推DP

    递推DP有两个需要明确,一个是递推公式,一个是初始化,这里的递推公式是写明了的,重要的就是初始化。

    在本题中每个元素的step是需要从更小的数得到的,因此需要排序原数组,从小到大去更新dp。

    时间复杂度:O(nd)

        public int maxJumps(int[] arr, int d) {
            int res = 1;
            int n = arr.length;
            int[] dp = new int[n];
            PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> Integer.compare(o1[1], o2[1]));
            for (int i = 0; i < n; i++) pq.add(new int[]{i, arr[i]});
            while (!pq.isEmpty()) {
                int[] curr = pq.poll();
                int idx = curr[0];
                int val = curr[1];
                dp[idx] = 1;
                for (int i = idx - 1; i >= Math.max(0, idx - d); i--) {
                    if (arr[i] >= val) break;
                    dp[idx] = Math.max(dp[idx], dp[i] + 1);
                }
                for (int i = idx + 1; i <= Math.min(n - 1, idx + d); i++) {
                    if (arr[i] >= val) break;
                    dp[idx] = Math.max(dp[idx], dp[i] + 1);
                }
                res = Math.max(res, dp[idx]);
            }
            return res;
        }
    

      

     

     

     

     

  • 相关阅读:
    20145222黄亚奇《Java程序设计》第4周学习总结
    20145222黄亚奇《Java程序设计》第3周学习总结
    20145222黄亚奇《Java程序设计》第2周学习总结
    CodeForces
    2020 Multi-University Training Contest 3 1008 Triangle Collision
    Educational Codeforces Round 92 (Rated for Div. 2) E. Calendar Ambiguity
    2020 Multi-University Training Contest 3 1006 X Number
    2020 Multi-University Training Contest 3 1004 Tokitsukaze and Multiple
    2020 Multi-University Training Contest 2 1007 In Search of Gold
    2020 Multi-University Training Contest 1 1009 Leading Robots
  • 原文地址:https://www.cnblogs.com/hyserendipity/p/12827302.html
Copyright © 2011-2022 走看看