文章目录
- [3607. 电网维护](#3607. 电网维护)
- [994. 腐烂的橘子](#994. 腐烂的橘子)
- [207. 课程表](#207. 课程表)
- [3542. 将所有元素变为 0 的最少操作次数](#3542. 将所有元素变为 0 的最少操作次数)
- [1094. 拼车](#1094. 拼车)
- [2960. 统计已测试设备](#2960. 统计已测试设备)
- [474. 一和零](#474. 一和零)
- [437. 路径总和 III](#437. 路径总和 III)
3607. 电网维护



sql
class Solution {
public int[] processQueries(int c, int[][] connections, int[][] queries) {
List<Integer>[] g = new ArrayList[c + 1];
Arrays.setAll(g, i -> new ArrayList<>());
for (int[] e : connections) {
int x = e[0], y = e[1];
g[x].add(y);
g[y].add(x);
}
int[] belong = new int[c + 1];
Arrays.fill(belong, -1);
List<PriorityQueue<Integer>> heaps = new ArrayList<>();
PriorityQueue<Integer> pq;
for (int i = 1; i <= c; i++) {
if (belong[i] >= 0) {
continue;
}
pq = new PriorityQueue<>();
dfs(i, g, belong, heaps.size(), pq);
heaps.add(pq);
}
int ansSize = 0;
for (int[] q : queries) {
if (q[0] == 1) {
ansSize++;
}
}
int[] ans = new int[ansSize];
int idx = 0;
boolean[] offline = new boolean[c + 1];
for (int[] q : queries) {
int x = q[1];
if (q[0] == 2) {
offline[x] = true;
continue;
}
if (!offline[x]) {
ans[idx++] = x;
continue;
}
pq = heaps.get(belong[x]);
// 懒删除:取堆顶的时候,如果离线,才删除
while (!pq.isEmpty() && offline[pq.peek()]) {
pq.poll();
}
ans[idx++] = pq.isEmpty() ? -1 : pq.peek();
}
return ans;
}
private void dfs(int x, List<Integer>[] g, int[] belong, int compId, PriorityQueue<Integer> pq) {
belong[x] = compId; // 记录节点 x 在哪个堆
pq.offer(x);
for (int y : g[x]) {
if (belong[y] < 0) {
dfs(y, g, belong, compId, pq);
}
}
}
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/power-grid-maintenance/solutions/3716402/dfs-lan-shan-chu-dui-pythonjavacgo-by-en-17gb/
994. 腐烂的橘子

多源BFS

sql
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 = 0;
while (fresh > 0 && !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 : ans;
}
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/rotting-oranges/solutions/2773461/duo-yuan-bfsfu-ti-dan-pythonjavacgojsrus-yfmh/
207. 课程表

sql
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
int[] indegrees = new int[numCourses];
List<List<Integer>> adjacency = new ArrayList<>();
Queue<Integer> queue = new LinkedList<>();
for(int i = 0; i < numCourses; i++)
adjacency.add(new ArrayList<>());
// Get the indegree and adjacency of every course.
for(int[] cp : prerequisites) {
indegrees[cp[0]]++;
adjacency.get(cp[1]).add(cp[0]);
}
// Get all the courses with the indegree of 0.
for(int i = 0; i < numCourses; i++)
if(indegrees[i] == 0) queue.add(i);
// BFS TopSort.
while(!queue.isEmpty()) {
int pre = queue.poll();
numCourses--;
for(int cur : adjacency.get(pre))
if(--indegrees[cur] == 0) queue.add(cur);
}
return numCourses == 0;
}
}
作者:Krahets
链接:https://leetcode.cn/problems/course-schedule/solutions/18806/course-schedule-tuo-bu-pai-xu-bfsdfsliang-chong-fa/

sql
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<List<Integer>> adjacency = new ArrayList<>();
for(int i = 0; i < numCourses; i++)
adjacency.add(new ArrayList<>());
int[] flags = new int[numCourses];
for(int[] cp : prerequisites)
adjacency.get(cp[1]).add(cp[0]);
for(int i = 0; i < numCourses; i++)
if(!dfs(adjacency, flags, i)) return false;
return true;
}
private boolean dfs(List<List<Integer>> adjacency, int[] flags, int i) {
if(flags[i] == 1) return false;
if(flags[i] == -1) return true;
flags[i] = 1;
for(Integer j : adjacency.get(i))
if(!dfs(adjacency, flags, j)) return false;
flags[i] = -1;
return true;
}
}
作者:Krahets
链接:https://leetcode.cn/problems/course-schedule/solutions/18806/course-schedule-tuo-bu-pai-xu-bfsdfsliang-chong-fa/
3542. 将所有元素变为 0 的最少操作次数




sql
class Solution {
public int minOperations(int[] nums) {
int ans = 0;
int top = -1; // 栈顶下标(把 nums 当作栈)
for (int x : nums) {
while (top >= 0 && x < nums[top]) {
top--; // 出栈
ans++;
}
// 如果 x 与栈顶相同,那么 x 与栈顶可以在同一次操作中都变成 0,x 无需入栈
if (top < 0 || x != nums[top]) {
nums[++top] = x; // 入栈
}
}
return ans + top + (nums[0] > 0 ? 1 : 0);
}
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/minimum-operations-to-convert-all-elements-to-zero/solutions/3673804/cong-fen-zhi-dao-dan-diao-zhan-jian-ji-x-mzbl/
1094. 拼车




sql
class Solution {
public boolean carPooling(int[][] trips, int capacity) {
int[] d = new int[1001];
for (int[] t : trips) {
int num = t[0], from = t[1], to = t[2];
d[from] += num;
d[to] -= num;
}
int s = 0;
for (int v : d) {
s += v;
if (s > capacity) {
return false;
}
}
return true;
}
}
class Solution {
public boolean carPooling(int[][] trips, int capacity) {
TreeMap<Integer, Integer> d = new TreeMap<>();
for (int[] t : trips) {
int num = t[0], from = t[1], to = t[2];
d.merge(from, num, Integer::sum);
d.merge(to, -num, Integer::sum);
}
int s = 0;
for (int v : d.values()) {
s += v;
if (s > capacity) {
return false;
}
}
return true;
}
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/car-pooling/solutions/2550264/suan-fa-xiao-ke-tang-chai-fen-shu-zu-fu-9d4ra/
2960. 统计已测试设备

初始化 dec=0,表示需要减一的次数。
设 x=batteryPercentages[i],那么该电池的实际百分比为 x−dec,如果 x−dec>0,即 x>dec,那么后面的数都要减一,根据差分数组的思想,把 dec 加一即可。
答案就是 dec。因为每次遇到 x>dec 都把 dec 加一,这正是题目要求统计的。
sql
class Solution {
public int countTestedDevices(int[] batteryPercentages) {
int dec = 0;
for (int x : batteryPercentages) {
if (x > dec) {
dec++;
}
}
return dec;
}
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/count-tested-devices-after-test-operations/solutions/2560949/on-zuo-fa-pythonjavacgo-by-endlesscheng-fc5k/
474. 一和零



sql
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int k = strs.length;
int[] cnt0 = new int[k];
for (int i = 0; i < k; i++) {
cnt0[i] = (int) strs[i].chars().filter(ch -> ch == '0').count();
}
int[][][] memo = new int[strs.length][m + 1][n + 1];
for (int[][] mat : memo) {
for (int[] arr : mat) {
Arrays.fill(arr, -1); // -1 表示没有计算过
}
}
return dfs(k - 1, m, n, strs, cnt0, memo);
}
private int dfs(int i, int j, int k, String[] strs, int[] cnt0, int[][][] memo) {
if (i < 0) {
return 0;
}
if (memo[i][j][k] != -1) { // 之前计算过
return memo[i][j][k];
}
// 不选 strs[i]
int res = dfs(i - 1, j, k, strs, cnt0, memo);
int cnt1 = strs[i].length() - cnt0[i];
if (j >= cnt0[i] && k >= cnt1) {
// 选 strs[i]
res = Math.max(res, dfs(i - 1, j - cnt0[i], k - cnt1, strs, cnt0, memo) + 1);
}
return memo[i][j][k] = res; // 记忆化
}
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/ones-and-zeroes/solutions/3038333/yi-bu-bu-si-kao-cong-ji-yi-hua-sou-suo-d-lqio/

sql
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int[][][] f = new int[strs.length + 1][m + 1][n + 1];
for (int i = 0; i < strs.length; i++) {
int cnt0 = (int) strs[i].chars().filter(ch -> ch == '0').count();
int cnt1 = strs[i].length() - cnt0;
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= n; k++) {
if (j >= cnt0 && k >= cnt1) {
f[i + 1][j][k] = Math.max(f[i][j][k], f[i][j - cnt0][k - cnt1] + 1);
} else {
f[i + 1][j][k] = f[i][j][k];
}
}
}
}
return f[strs.length][m][n];
}
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/ones-and-zeroes/solutions/3038333/yi-bu-bu-si-kao-cong-ji-yi-hua-sou-suo-d-lqio/
437. 路径总和 III

一定要先看前置题目: 560. 和为 K 的子数组

sql
class Solution {
public int pathSum(TreeNode root, int targetSum) {
Map<Long, Integer> cnt = new HashMap<>();
cnt.put(0L, 1);
return dfs(root, 0, targetSum, cnt);
}
private int dfs(TreeNode node, long s, int targetSum, Map<Long, Integer> cnt) {
if (node == null) {
return 0;
}
s += node.val;
// 把 node 当作路径的终点,统计有多少个起点
int ans = cnt.getOrDefault(s - targetSum, 0);
cnt.merge(s, 1, Integer::sum); // cnt[s]++
ans += dfs(node.left, s, targetSum, cnt);
ans += dfs(node.right, s, targetSum, cnt);
cnt.merge(s, -1, Integer::sum); // cnt[s]-- 恢复现场(撤销 cnt[s]++)
return ans;
}
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/path-sum-iii/solutions/2784856/zuo-fa-he-560-ti-shi-yi-yang-de-pythonja-fmzo/
问:为什么递归参数 s 不需要恢复现场?
答:s 是基本类型,在函数调用的时候会复制一份往下传递,s += node.val 修改的仅仅是当前递归函数中的 s 参数,并不会影响到其他递归函数中的 s。注:如果把 s 放在递归函数外,此时只有一个 s,执行 s += node.val 就会影响全局了。

