LeetCode﹝堆ி﹞移除石子的最大得分,吃苹果的最大数目等

Posted 白鳯

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode﹝堆ி﹞移除石子的最大得分,吃苹果的最大数目等相关的知识,希望对你有一定的参考价值。

【LeetCode】﹝堆ி﹞移除石子的最大得分,吃苹果的最大数目等

移除石子的最大得分★★

1753. 移除石子的最大得分

题目】你正在玩一个单人游戏,面前放置着大小分别为 abc 的 三堆 石子。

每回合你都要从两个 不同的非空堆 中取出一颗石子,并在得分上加 1 分。当存在 两个或更多 的空堆时,游戏停止。

给你三个整数 abc ,返回可以得到的 最大分数 。

提示:

  • 1 <= a, b, c <= 105

示例

输入:a = 2, b = 4, c = 6
输出:6
解释:石子起始状态是 (2, 4, 6) ,最优的一组操作是:
- 从第一和第三堆取,石子状态现在是 (1, 4, 5)
- 从第一和第三堆取,石子状态现在是 (0, 4, 4)
- 从第二和第三堆取,石子状态现在是 (0, 3, 3)
- 从第二和第三堆取,石子状态现在是 (0, 2, 2)
- 从第二和第三堆取,石子状态现在是 (0, 1, 1)
- 从第二和第三堆取,石子状态现在是 (0, 0, 0)
总分:6 分 。

解题思路

方法一:排序

class Solution {
    public int maximumScore(int a, int b, int c) {
        int score = 0;
        int[] nums = {a, b, c};
        Arrays.sort(nums);
        while (nums[0] > 0 || nums[1] > 0) {
            nums[1]--;
            nums[2]--;
            score += 1;
            Arrays.sort(nums);
        }
        return score;
    }
}

方法二:优先队列

class Solution {
    public int maximumScore(int a, int b, int c) {
        int score = 0;
        //大根堆
        PriorityQueue<Integer> queue = new PriorityQueue<Integer>( (o1, o2) -> {
            return o2 - o1;
        });
        queue.offer(a);
        queue.offer(b);
        queue.offer(c);
        while (true) {
            int max = queue.poll();
            int mid = queue.poll();
            if (mid == 0) {
                break;
            }
            score += 1;
            queue.offer(mid - 1);
            queue.offer(max - 1);
        }
        return score;
    }
}

方法三:数学

class Solution {
    public int maximumScore(int a, int b, int c) {
        int[] nums = {a, b, c};
        Arrays.sort(nums);
        if (nums[0] + nums[1] < nums[2]) {
            return nums[0] + nums[1];
        }
        //和为偶数全全空,和为奇数剩一个
        return (a + b + c) / 2;
    }
}

参考热评@班长夏四果题解


魔塔游戏★★

LCP 30. 魔塔游戏

题目】小扣当前位于魔塔游戏第一层,共有 N 个房间,编号为 0 ~ N-1。每个房间的补血道具/怪物对于血量影响记于数组 nums,其中正数表示道具补血数值,即血量增加对应数值;负数表示怪物造成伤害值,即血量减少对应数值;0 表示房间对血量无影响。

小扣初始血量为 1,且无上限。假定小扣原计划按房间编号升序访问所有房间补血/打怪,为保证血量始终为正值,小扣需对房间访问顺序进行调整,每次仅能将一个怪物房间(负数的房间)调整至访问顺序末尾。请返回小扣最少需要调整几次,才能顺利访问所有房间。若调整顺序也无法访问完全部房间,请返回 -1

提示:

  • 1 <= nums.length <= 10^5
  • -10^5 <= nums[i] <= 10^5

示例

输入:nums = [100,100,100,-250,-60,-140,-50,-50,100,150]
输出:1
解释:初始血量为 1。至少需要将 nums[3] 调整至访问顺序末尾以满足要求
---------------------------------------------------------
输入:nums = [-200,-300,400,0]
输出:-1
解释:调整访问顺序也无法完成全部房间的访问

解题思路

贪心+优先队列

  • 计算数组和,若小于1则不能访问完,返回-1

  • 堆中保存打怪值(即负数),当当前生命值为负数时,从堆中出一个最小的(贪心思想,绝对值最大)放在数组末尾,调整值+1

class Solution {
    public int magicTower(int[] nums) {
        int sum = 1;
        for (int num : nums) sum += num;
        if (sum < 1) return -1;
        long blood = 1;
        int count = 0;
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        for (int num : nums) {
            blood += num;
            if (num < 0) queue.offer(num);
            if (blood < 1) {
                while (blood < 1 && !queue.isEmpty()) {
                    blood -= queue.poll();
                    count++;
                }
            }
        }
        return count;
    }
}

吃苹果的最大数目★★

1705. 吃苹果的最大数目

题目】有一棵特殊的苹果树,一连 n 天,每天都可以长出若干个苹果。在第 i 天,树上会长出 apples[i] 个苹果,这些苹果将会在 days[i] 天后(也就是说,第 i + days[i] 天时)腐烂,变得无法食用。也可能有那么几天,树上不会长出新的苹果,此时用 apples[i] == 0days[i] == 0 表示。

你打算每天 最多 吃一个苹果来保证营养均衡。注意,你可以在这 n 天之后继续吃苹果。

给你两个长度为 n 的整数数组 daysapples ,返回你可以吃掉的苹果的最大数目。

提示:

  • apples.length == n
  • days.length == n
  • 1 <= n <= 2 * 104
  • 0 <= apples[i], days[i] <= 2 * 104
  • 只有在 apples[i] = 0 时,days[i] = 0 才成立

示例

输入:apples = [1,2,3,5,2], days = [3,2,1,4,2]
输出:7
解释:你可以吃掉 7 个苹果:
- 第一天,你吃掉第一天长出来的苹果。
- 第二天,你吃掉一个第二天长出来的苹果。
- 第三天,你吃掉一个第二天长出来的苹果。过了这一天,第三天长出来的苹果就已经腐烂了。
- 第四天到第七天,你吃的都是第四天长出来的苹果。

解题思路

class Solution {
    public int eatenApples(int[] apples, int[] days) {
        PriorityQueue<int[]> queue = new PriorityQueue<int[]>( (a, b) -> {
            return a[0] - b[0];
        });
        int count = 0, i = 0;
        while (true) {
            if (i < apples.length) {
                queue.offer(new int[]{days[i] + i, apples[i]});
            } else if (i >= apples.length && queue.isEmpty()) {
                break;
            }
            //移除腐烂的苹果
            while (!queue.isEmpty() && (queue.peek()[0] <= i || queue.peek()[1] == 0)) {
                queue.poll();
            }
            //吃苹果
            if (!queue.isEmpty()) {
                count++;
                queue.peek()[1]--;
            }
            //天数+1
            i++;
        }
        return count;
    }
}

合并K个升序链表★★★

23. 合并K个升序链表

题目】给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

提示

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] 按 升序 排列
  • lists[i].length 的总和不超过 10^4

示例

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

解题思路

归并排序的解法见往期博客【LeetCode】﹝归并思想ி﹞逆序对、翻转对、排序链表、合并K个升序链表

优先队列:采用小根堆

将链表的头结点加入队列中,每次选最小的,最后将指针后移一位加入优先队列,若空则不必加入。

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) {
            return null;
        }
        
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        PriorityQueue<ListNode> queue = new PriorityQueue<ListNode>((o1, o2) -> {
            return o1.val - o2.val;
        });

        for (ListNode list : lists) {
            if (list != null) {
                queue.offer(list);
            }
        }

        while (!queue.isEmpty()) {
            ListNode head = queue.poll();
            cur.next = head;
            if (head.next != null) {
                queue.offer(head.next);
            }
            cur = cur.next;
        }

        return dummy.next;
    }
}

水位上升的泳池中游泳★★★

778. 水位上升的泳池中游泳

题目】在一个 N x N 的坐标方格 grid 中,每一个方格的值 grid[i][j] 表示在位置 (i,j) 的平台高度。

现在开始下雨了。当时间为 t 时,此时雨水导致水池中任意位置的水位为 t 。你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。当然,在你游泳的时候你必须待在坐标方格里面。

你从坐标方格的左上平台 (0,0) 出发。最少耗时多久你才能到达坐标方格的右下平台 (N-1, N-1)

提示:

  1. 2 <= N <= 50
  2. grid[i][j][0, ..., N*N - 1] 的排列

示例

输入: [[0,2],[1,3]]
输出: 3
解释:
时间为0时,你位于坐标方格的位置为 (0, 0)。
此时你不能游向任意方向,因为四个相邻方向平台的高度都大于当前时间为 0 时的水位。
等时间到达 3 时,你才可以游向平台 (1, 1). 因为此时的水位是 3,坐标方格中的平台没有比水位 3 更高的,所以你可以游向坐标方格中的任意位置          

解题思路

方法一:优先队列

class Solution {
    public int swimInWater(int[][] grid) {
        int n = grid.length;
        int[][] dp = new int[n][n];
        int[] dirs = {-1, 0, 1, 0, -1};
        //堆中的三元组[i, j, height]即下标和高度
        PriorityQueue<int[]> heap = new PriorityQueue<int[]>((o1, o2) -> {
            return o1[2] - o2[2];
        });

        //初始化
        for (int[] p : dp) {
            Arrays.fill(p, 10000);
        }
        dp[0][0] = grid[0][0];
        heap.offer(new int[]{0, 0, grid[0][0]});

        while (!heap.isEmpty()) {
            int[] cur = heap.poll();
            int x = cur[0], y = cur[1];
            if (x == n - 1 && y == n - 1) {
                break;
            }

            for (int i = 0; i < 4; i++) {
                int posx = x + dirs[i];
                int posy = y + dirs[i + 1];
                if (posx >= 0 && posx < n && posy >= 0 && posy < n) {
                    int h = Math.max(grid[posx][posy], dp[x][y]);
                    if (h < dp[posx][posy]) {
                        dp[posx][posy] = h;
                        heap.offer(new int[]{posx, posy, h});
                    }
                }
            }
        }

        return dp[n - 1][n - 1];
    }
}

方法二:并查集

class Solution {
    public int swimInWater(int[][] grid) {
        int N = grid.length;
        int len = N * N;
        int[] index = new int[len];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                index[grid[i][j]] = i * N + j;
            }
        }
        int[] dirs = {-1, 0, 1, 0, -1};
        UnionFind uf = new UnionFind(len);
        
        for (int i = 0; i < len; i++) {
            int x = index[i] / N;
            int y = index[i] % N;

            for (int j = 0; j < 4; j++) LeetCode 1753. 移除石子的最大得分

1753. 移除石子的最大得分

Leetcode 5379. 石子游戏 III(第183场周赛)

石子合并问题

石子合并问题

石子合并问题