【题解】—— LeetCode一周小结20

🌟欢迎来到 我的博客 —— 探索技术的无限可能!


🌟博客的简介(文章目录)


【题解】—— 每日一道题目栏


上接:【题解】—— LeetCode一周小结19

13.腐烂的橘子

题目链接:994. 腐烂的橘子

在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:

值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。

返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。

示例 1:
在这里插入图片描述

输入:grid = [[2,1,1],[1,1,0],[0,1,1]]

输出:4

示例 2:

输入:grid = [[2,1,1],[0,1,1],[1,0,1]]

输出:-1

解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个方向上。

示例 3:

输入:grid = [[0,2]]

输出:0

解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。

提示:

m == grid.length

n == grid[i].length

1 <= m, n <= 10

grid[i][j] 仅为 0、1 或 2

题解:
方法:广度优先搜索
        
示例 1可以看出:

  1. 统计所有初始就腐烂的橘子的位置,加到列表 qqq 中,现在 q=[(0,0)]q = [(0,0)]q=[(0,0)]
  2. 初始化答案 ans=−1\textit{ans}=-1ans=1。模拟橘子腐烂的过程,不断循环,直到 qqq 为空。
  3. 答案加一,在第 ans=0\textit{ans}=0ans=0 分钟,遍历 qqq 中橘子的四方向相邻的新鲜橘子,把这些橘子腐烂,qqq 更新为这些橘子的位置,现在 q=[(0,1),(1,0)]q = [(0,1),(1,0)]q=[(0,1),(1,0)]
  4. 答案加一,在第 ans=1\textit{ans}=1ans=1 分钟,遍历 qqq 中橘子的四方向相邻的新鲜橘子,把这些橘子腐烂,qqq 更新为这些橘子的位置,现在 q=[(0,2),(1,1)]q = [(0,2),(1,1)]q=[(0,2),(1,1)]
  5. 答案加一,在第 ans=2\textit{ans}=2ans=2 分钟,遍历 qqq 中橘子的四方向相邻的新鲜橘子,把这些橘子腐烂,qqq 更新为这些橘子的位置,现在 q=[(2,1)]q = [(2,1)]q=[(2,1)]
  6. 答案加一,在第 ans=3\textit{ans}=3ans=3 分钟,遍历 qqq 中橘子的四方向相邻的新鲜橘子,把这些橘子腐烂,qqq 更新为这些橘子的位置,现在 q=[(2,2)]q = [(2,2)]q=[(2,2)]
  7. 答案加一,在第 ans=4\textit{ans}=4ans=4 分钟,遍历 qqq 中橘子的四方向相邻的新鲜橘子,把这些橘子腐烂,qqq 更新为这些橘子的位置,由于没有这样的橘子,q=[]q = []q=[],退出循环。

为了判断是否有永远不会腐烂的橘子(如示例 2),我们可以统计初始新鲜橘子的个数 fresh\textit{fresh}fresh。在 BFS 中,每有一个新鲜橘子被腐烂,就把 fresh\textit{fresh}fresh 减一,这样最后如果发现 fresh>0\textit{fresh}>0fresh>0,就意味着有橘子永远不会腐烂,返回 −1-11

为了判断是否有永远不会腐烂的橘子(如示例 2),我们可以统计初始新鲜橘子的个数 fresh\textit{fresh}fresh。在 BFS 中,每有一个新鲜橘子被腐烂,就把 fresh\textit{fresh}fresh 减一,这样最后如果发现 fresh>0\textit{fresh}>0fresh>0,就意味着有橘子永远不会腐烂,返回 −1-11

代码实现时,在 BFS 中要将 grid[i][j]=1\textit{grid}[i][j]=1grid[i][j]=1 的橘子修改成 222(或者其它不等于 111 的数),这可以保证每个橘子加入 qqq 中至多一次。如果不修改,我们就无法知道哪些橘子被腐烂过了,比如示例 1 中 (0,1)(0,1)(0,1) 去腐烂 (1,1)(1,1)(1,1),而 (1,1)(1,1)(1,1) 在此之后又重新腐烂 (0,1)(0,1)(0,1),如此反复,程序就会陷入死循环。读者可以注释掉下面代码中的 grid[i][j] = 2 这行代码试试。

class Solution {
    private static final int[][] DIRECTIONS = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 四方向

    public int orangesRotting(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int fresh = 0;
        List<int[]> q = new ArrayList<>();
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 1) {
                    fresh++; // 统计新鲜橘子个数
                } else if (grid[i][j] == 2) {
                    q.add(new int[]{i, j}); // 一开始就腐烂的橘子
                }
            }
        }

        int ans = -1;
        while (!q.isEmpty()) {
            ans++; // 经过一分钟
            List<int[]> tmp = q;
            q = new ArrayList<>();
            for (int[] pos : tmp) { // 已经腐烂的橘子
                for (int[] d : DIRECTIONS) { // 四方向
                    int i = pos[0] + d[0];
                    int j = pos[1] + d[1];
                    if (0 <= i && i < m && 0 <= j && j < n && grid[i][j] == 1) { // 新鲜橘子
                        fresh--;
                        grid[i][j] = 2; // 变成腐烂橘子
                        q.add(new int[]{i, j});
                    }
                }
            }
        }

        return fresh > 0 ? -1 : Math.max(ans, 0);
    }
}

14.完成所有任务需要的最少轮数

题目链接:2244. 完成所有任务需要的最少轮数

给你一个下标从 0 开始的整数数组 tasks ,其中 tasks[i] 表示任务的难度级别。在每一轮中,你可以完成 2 个或者 3 个 相同难度级别 的任务。

返回完成所有任务需要的 最少 轮数,如果无法完成所有任务,返回 -1 。

示例 1:

输入:tasks = [2,2,3,3,2,4,4,4,4,4]

输出:4

解释:要想完成所有任务,一个可能的计划是:

  • 第一轮,完成难度级别为 2 的 3 个任务。
  • 第二轮,完成难度级别为 3 的 2 个任务。
  • 第三轮,完成难度级别为 4 的 3 个任务。
  • 第四轮,完成难度级别为 4 的 2 个任务。

可以证明,无法在少于 4 轮的情况下完成所有任务,所以答案为 4 。

示例 2:

输入:tasks = [2,3,3]

输出:-1

解释:难度级别为 2 的任务只有 1 个,但每一轮执行中,只能选择完成 2 个或者 3 个相同难度级别的任务。因此,无法完成所有任务,答案为
-1 。

提示:

1 <= tasks.length <= 105

1 <= tasks[i] <= 109

题解:
方法:贪心
        

public class Solution {
    public int minimumRounds(int[] tasks) {
        Map<Integer, Integer> cnt = new HashMap<>();
        for (int t : tasks) {
            cnt.merge(t, 1, Integer::sum);
        }
        int ans = 0;
        for (int c : cnt.values()) {
            if (c == 1) {
                return -1;
            }
            ans += (c + 2) / 3;
        }
        return ans;
    }
}


15.完成所有任务的最少时间

题目链接:2589. 完成所有任务的最少时间

你有一台电脑,它可以 同时 运行无数个任务。给你一个二维整数数组 tasks ,其中 tasks[i] = [starti, endi, durationi] 表示第 i 个任务需要在 闭区间 时间段 [starti, endi] 内运行 durationi 个整数时间点(但不需要连续)。

当电脑需要运行任务时,你可以打开电脑,如果空闲时,你可以将电脑关闭。

请你返回完成所有任务的情况下,电脑最少需要运行多少秒。

示例 1:

输入:tasks = [[2,3,1],[4,5,1],[1,5,2]]

输出:2

解释:

  • 第一个任务在闭区间 [2, 2] 运行。
  • 第二个任务在闭区间 [5, 5] 运行。
  • 第三个任务在闭区间 [2, 2] 和 [5, 5] 运行。

电脑总共运行 2 个整数时间点。

示例 2:

输入:tasks = [[1,3,2],[2,5,3],[5,6,2]]

输出:4

解释:

  • 第一个任务在闭区间 [2, 3] 运行
  • 第二个任务在闭区间 [2, 3] 和 [5, 5] 运行。
  • 第三个任务在闭区间 [5, 6] 运行。

电脑总共运行 4 个整数时间点。

提示:

1 <= tasks.length <= 2000

tasks[i].length == 3

1 <= starti, endi <= 2000

1 <= durationi <= endi - starti + 1

题解:
方法:贪心+暴力

按照区间右端点从小到大排序。

排序后,对于区间 tasks[i]\textit{tasks}[i]tasks[i] 来说,它右侧的任务区间要么和它没有交集,要么包含它的一部分后缀

例如排序后的区间为 [1,5],[3,7],[6,8][1,5],[3,7],[6,8][1,5],[3,7],[6,8],对于 [1,5][1,5][1,5] 来说,它右边的区间要么和它没有交集,例如 [6,8][6,8][6,8];要么交集是 [1,5][1,5][1,5] 的后缀,例如 [1,5][1,5][1,5][3,7][3,7][3,7] 的交集是 [3,5][3,5][3,5],这是 [1,5][1,5][1,5] 的后缀(3,4,53,4,53,4,51,2,3,4,51,2,3,4,51,2,3,4,5 的后缀)。

遍历排序后的任务,先统计区间内的已运行的电脑运行时间点,如果个数小于 duration\textit{duration}duration,则需要新增时间点。根据提示 2,尽量把新增的时间点安排在区间 [start,end][\textit{start},\textit{end}][start,end] 的后缀上,这样下一个区间就能统计到更多已运行的时间点。

class Solution {
    public int findMinimumTime(int[][] tasks) {
        Arrays.sort(tasks, (a, b) -> a[1] - b[1]);
        int ans = 0;
        int mx = tasks[tasks.length - 1][1];
        boolean[] run = new boolean[mx + 1];
        for (int[] t : tasks) {
            int start = t[0];
            int end = t[1];
            int d = t[2];
            for (int i = start; i <= end; i++) {
                if (run[i]) {
                    d--; // 去掉运行中的时间点
                }
            }
            for (int i = end; d > 0; i--) { // 剩余的 d 填充区间后缀
                if (!run[i]) {
                    run[i] = true; // 运行
                    d--;
                    ans++;
                }
            }
        }
        return ans;
    }
}

16.你可以工作的最大周数

题目链接:1953. 你可以工作的最大周数

给你 n 个项目,编号从 0 到 n - 1 。同时给你一个整数数组 milestones ,其中每个 milestones[i] 表示第 i 个项目中的阶段任务数量。

你可以按下面两个规则参与项目中的工作:

每周,你将会完成 某一个 项目中的 恰好一个 阶段任务。你每周都 必须 工作。
在 连续的 两周中,你 不能 参与并完成同一个项目中的两个阶段任务。
一旦所有项目中的全部阶段任务都完成,或者仅剩余一个阶段任务都会导致你违反上面的规则,那么你将 停止工作 。注意,由于这些条件的限制,你可能无法完成所有阶段任务。

返回在不违反上面规则的情况下你 最多 能工作多少周。

示例 1:

输入:milestones = [1,2,3]

输出:6

解释:一种可能的情形是: ​​​​- 第 1 周,你参与并完成项目 0 中的一个阶段任务。

  • 第 2 周,你参与并完成项目 2 中的一个阶段任务。
  • 第 3 周,你参与并完成项目 1 中的一个阶段任务。
  • 第 4 周,你参与并完成项目 2 中的一个阶段任务。
  • 第 5 周,你参与并完成项目 1 中的一个阶段任务。
  • 第 6 周,你参与并完成项目 2 中的一个阶段任务。

总周数是 6 。

示例 2:

输入:milestones = [5,2,1]

输出:7

解释:一种可能的情形是:

  • 第 1 周,你参与并完成项目 0 中的一个阶段任务。
  • 第 2 周,你参与并完成项目 1 中的一个阶段任务。
  • 第 3 周,你参与并完成项目 0 中的一个阶段任务。
  • 第 4 周,你参与并完成项目 1 中的一个阶段任务。
  • 第 5 周,你参与并完成项目 0 中的一个阶段任务。
  • 第 6 周,你参与并完成项目 2 中的一个阶段任务。
  • 第 7 周,你参与并完成项目 0 中的一个阶段任务。

总周数是 7 。

注意,你不能在第 8 周参与完成项目 0 中的最后一个阶段任务,因为这会违反规则。

因此,项目 0 中会有一个阶段任务维持未完成状态。

提示:

n == milestones.length

1 <= n <= 105

1 <= milestones[i] <= 109

题解:
方法:贪心
        

class Solution {
    public long numberOfWeeks(int[] milestones) {
        long s = 0;
        int m = 0;
        for (int x : milestones) {
            s += x;
            m = Math.max(m, x);
        }
        return m > s - m + 1 ? (s - m) * 2 + 1 : s;
    }
}

17.安排工作以达到最大收益

题目链接:826. 安排工作以达到最大收益

你有 n 个工作和 m 个工人。给定三个数组: difficulty, profit 和 worker ,其中:

difficulty[i] 表示第 i 个工作的难度,profit[i] 表示第 i 个工作的收益。
worker[i] 是第 i 个工人的能力,即该工人只能完成难度小于等于 worker[i] 的工作。
每个工人 最多 只能安排 一个 工作,但是一个工作可以 完成多次 。

举个例子,如果 3 个工人都尝试完成一份报酬为 $1 的同样工作,那么总收益为 $3 。如果一个工人不能完成任何工作,他的收益为 $0 。
返回 在把工人分配到工作岗位后,我们所能获得的最大利润 。

示例 1:

输入: difficulty = [2,4,6,8,10], profit = [10,20,30,40,50], worker =
[4,5,6,7]

输出: 100

解释: 工人被分配的工作难度是 [4,4,6,6] ,分别获得 [20,20,30,30] 的收益。

示例 2:

输入: difficulty = [85,47,57], profit = [24,66,99], worker = [40,25,25]

输出: 0

提示:

n == difficulty.length

n == profit.length

m == worker.length

1 <= n, m <= 104

1 <= difficulty[i], profit[i], worker[i] <= 105

题解:
方法:排序

如果把 worker\textit{worker}worker 按照从小到大的顺序排序,那么第 iii 个工人能做的工作,他右边的(worker\textit{worker}worker 值更大的)工人也能做。这意味着,如果我们遍历了所有 difficulty[j]≤worker[i]\textit{difficulty}[j]\le \textit{worker}[i]difficulty[j]worker[i] 的工作,那么从第 iii 个工人到第 i+1i+1i+1 个工人,只需要额外遍历 worker[i]<difficulty[j]≤worker[i+1]\textit{worker}[i] < \textit{difficulty}[j]\le \textit{worker}[i+1]worker[i]<difficulty[j]worker[i+1] 的工作。

difficulty\textit{difficulty}difficultyprofit\textit{profit}profit 绑在一起,按照 difficulty\textit{difficulty}difficulty 从小到大排序,我们可以在遍历 worker\textit{worker}worker 的同时,用双指针遍历并维护 difficulty[j]≤worker[i]\textit{difficulty}[j]\le \textit{worker}[i]difficulty[j]worker[i] 的最大的 profit[j]\textit{profit}[j]profit[j],即为第 iii 个工人所能获得的最大利润。累加每名工人能获得的最大利润,即为答案。

class Solution {
    public int maxProfitAssignment(int[] difficulty, int[] profit, int[] worker) {
        int n = difficulty.length;
        int[][] jobs = new int[n][2];
        for (int i = 0; i < n; i++) {
            jobs[i][0] = difficulty[i];
            jobs[i][1] = profit[i];
        }
        Arrays.sort(jobs, (a, b) -> a[0] - b[0]);
        Arrays.sort(worker);
        int ans = 0, j = 0, maxProfit = 0;
        for (int w : worker) {
            while (j < n && jobs[j][0] <= w) {
                maxProfit = Math.max(maxProfit, jobs[j++][1]);
            }
            ans += maxProfit;
        }
        return ans;
    }
}

18.找出可整除性得分最大的整数

题目链接:2644. 找出可整除性得分最大的整数

给你两个下标从 0 开始的整数数组 nums 和 divisors 。

divisors[i] 的 可整除性得分 等于满足 nums[j] 能被 divisors[i] 整除的下标 j 的数量。

返回 可整除性得分 最大的整数 divisors[i] 。如果有多个整数具有最大得分,则返回数值最小的一个。

示例 1:

输入:nums = [4,7,9,3,9], divisors = [5,2,3]

输出:3

解释:divisors 中每个元素的可整除性得分为:

divisors[0] 的可整除性得分为 0 ,因为 nums 中没有任何数字能被 5 整除。

divisors[1] 的可整除性得分为 1 ,因为 nums[0] 能被 2 整除。

divisors[2] 的可整除性得分为 3 ,因为 nums[2]、nums[3] 和 nums[4] 都能被 3 整除。

因此,返回 divisors[2] ,它的可整除性得分最大。

示例 2:

输入:nums = [20,14,21,10], divisors = [5,7,5]

输出:5

解释:divisors 中每个元素的可整除性得分为:

divisors[0] 的可整除性得分为 2 ,因为 nums[0] 和 nums[3] 都能被 5 整除。

divisors[1] 的可整除性得分为 2 ,因为 nums[1] 和 nums[2] 都能被 7 整除。

divisors[2] 的可整除性得分为 2 ,因为 nums[0] 和 nums[3] 都能被5整除。

由于 divisors[0]、divisors[1] 和 divisors[2] 的可整除性得分都是最大的,因此,我们返回数值最小的一个,即
divisors[2] 。

示例 3:

输入:nums = [12], divisors = [10,16]

输出:10

解释:divisors 中每个元素的可整除性得分为:

divisors[0] 的可整除性得分为 0 ,因为 nums 中没有任何数字能被 10 整除。

divisors[1] 的可整除性得分为 0 ,因为 nums 中没有任何数字能被 16 整除。

由于 divisors[0] 和 divisors[1] 的可整除性得分都是最大的,因此,我们返回数值最小的一个,即 divisors[0]

提示:

1 <= nums.length, divisors.length <= 1000

1 <= nums[i], divisors[i] <= 109

题解:
方法:枚举

我们可以枚举 divisorsdivisorsdivisors 中的每个元素 divdivdiv,计算 numsnumsnums 中有多少个元素能被 divdivdiv 整除,记为 cntcntcnt

  • 如果 cntcntcnt 大于当前最大的可整除性得分 mxmxmx,则更新 mx=cntmx = cntmx=cnt,并且更新 ans=divans = divans=div
  • 如果 cntcntcnt 等于 mxmxmx 并且 divdivdiv 小于 ansansans,则更新 ans=divans = divans=div

最后返回 ansansans 即可。

class Solution {
    public int maxDivScore(int[] nums, int[] divisors) {
        int ans = divisors[0];
        int mx = 0;
        for (int div : divisors) {
            int cnt = 0;
            for (int x : nums) {
                if (x % div == 0) {
                    ++cnt;
                }
            }
            if (mx < cnt) {
                mx = cnt;
                ans = div;
            } else if (mx == cnt) {
                ans = Math.min(ans, div);
            }
        }
        return ans;
    }
}

19.找出数组游戏的赢家

题目链接:1535. 找出数组游戏的赢家

给你一个由 不同 整数组成的整数数组 arr 和一个整数 k 。

每回合游戏都在数组的前两个元素(即 arr[0] 和 arr[1] )之间进行。比较 arr[0] 与 arr[1] 的大小,较大的整数将会取得这一回合的胜利并保留在位置 0 ,较小的整数移至数组的末尾。当一个整数赢得 k 个连续回合时,游戏结束,该整数就是比赛的 赢家 。

返回赢得比赛的整数。

题目数据 保证 游戏存在赢家。

示例 1:

输入:arr = [2,1,3,5,4,6,7], k = 2

输出:5

解释:一起看一下本场游戏每回合的情况:
在这里插入图片描述
因此将进行 4 回合比赛,其中 5 是赢家,因为它连胜 2 回合。

示例 2:

输入:arr = [3,2,1], k = 10

输出:3

解释:3 将会在前 10 个回合中连续获胜。 示例 3:

输入:arr = [1,9,8,2,3,7,6,4,5], k = 7

输出:9

示例 4:

输入:arr = [1,11,22,33,44,55,66,77,88,99], k = 1000000000

输出:99

提示:

2 <= arr.length <= 10^5

1 <= arr[i] <= 10^6

arr 所含的整数 各不相同 。

1 <= k <= 10^9

题解:
方法:模拟
        

class Solution {
    public int getWinner(int[] arr, int k) {
        int mx = arr[0];
        int win = 0;
        for (int i = 1; i < arr.length && win < k; i++) {
            if (arr[i] > mx) { // 新的最大值
                mx = arr[i];
                win = 0;
            }
            win++; // 获胜回合 +1
        }
        return mx;
    }
}

下接:【题解】—— LeetCode一周小结21


  • 27
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZShiJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值