🌟欢迎来到 我的博客 —— 探索技术的无限可能!
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
题解:
方法:广度优先搜索
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
题解:
方法:贪心+暴力
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
题解:
方法:排序
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
题解:
方法:枚举
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;
}
}