拓扑排序扩展
⭐ 重要技巧
利用拓扑排序的过程,上游节点逐渐推送消息给下游节点的技巧
⚠️ 注意
这个技巧已经是树型dp的内容了,不过即便不会动态规划,本节课也能听懂动态规划专题(包括树型dp)会在后续【必备】课程里讲述
最大食物链计数
题目链接
https://www.luogu.com.cn/problem/P4017
思路分析
以每个节点看作结尾,记录有多少条路径可以到达该节点,将节点的信息推送给与之相连的下一个节点,依次累加统计,得到最终总的食物链条数,采用链式前向星建图
代码实现
java
import java.io.*;
import java.util.Arrays;
public class Main {
public static int MAXN = 5001;
public static int MAXM = 500001;
public static int MOD = 80112002;
// 链式前向星建图
public static int[] head = new int[MAXN];
public static int[] next = new int[MAXM];
public static int[] to = new int[MAXM];
public static int cnt;
// 拓扑排序需要的队列
public static int[] queue = new int[MAXN];
// 拓扑排序需要的入度表
public static int[] indegree = new int[MAXN];
// 拓扑排序需要的推送信息
public static int[] lines = new int[MAXN];
public static int n, m;
public static void build(int n) {
cnt = 1;
Arrays.fill(indegree, 0, n + 1, 0);
Arrays.fill(lines, 0, n + 1, 0);
Arrays.fill(head, 0, n + 1, 0);
}
// 采用头插,u -> v
public static void addEdge(int u, int v) {
// 新的 edge 的 next 伟原来的 head
next[cnt] = head[u];
to[cnt] = v;
// 更新新的 head
head[u] = cnt++;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
while (in.nextToken() != StreamTokenizer.TT_EOF) {
n = (int) in.nval;
in.nextToken();
m = (int) in.nval;
build(n);
for (int i = 0; i < m; i++) {
in.nextToken();
int u = (int) in.nval;
in.nextToken();
int v = (int) in.nval;
// u -> v
addEdge(u, v);
indegree[v]++;
}
out.println(ways());
}
out.flush();
out.close();
br.close();
}
public static int ways() {
int l = 0;
int r = 0;
// 初始化
for (int i = 1; i <= n; i++) {
if (indegree[i] == 0) {
queue[r++] = i;
lines[i] = 1;
}
}
int ans = 0;
// 拓扑排序
while (l < r) {
int u = queue[l++];
// 头边的边号为 0,表示当前节点不再有后续的邻居了
if (head[u] == 0) {
// 同于原理
ans = (ans + lines[u]) % MOD;
} else {
for (int ei = head[u]; ei > 0; ei = next[ei]) {
// u -> v
int v = to[ei];
lines[v] = (lines[v] + lines[u]) % MOD;
if (--indegree[v] == 0) {
queue[r++] = v;
}
}
}
}
return ans;
}
}喧闹和富有
题目链接
https://leetcode.cn/problems/loud-and-rich/
思路分析
若 a 比 b 有钱则认为有一条 a 指向 b 的边,题目要求返回 ans 数组,i 位置往 i + 1 位置推送信息,如果发现 i 位置的安静值更小,则更新 i + 1 位置的值 ans [ i + 1 ] 为 ans [ i ]
代码实现
java
class Solution {
public int[] loudAndRich(int[][] richer, int[] quiet) {
int n = quiet.length;
// 邻接表建图
List<List<Integer>> graph = new ArrayList<>();
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
// 构建邻接表
int[] indegree = new int[n];
for (int[] r : richer) {
graph.get(r[0]).add(r[1]);
indegree[r[1]]++;
}
int[] queue = new int[n];
int l = 0;
int r = 0;
for (int i = 0; i < n; i++) {
if (indegree[i] == 0) {
queue[r++] = i;
}
}
int[] ans = new int[n];
// 初始化
for (int i = 0; i < n; i++) {
ans[i] = i;
}
// 拓扑排序
while (l < r) {
int cur = queue[l++];
for (int next : graph.get(cur)) {
if (quiet[ans[next]] > quiet[ans[cur]]) {
ans[next] = ans[cur];
}
if (--indegree[next] == 0) {
queue[r++] = next;
}
}
}
return ans;
}
}并行课程
题目链接
https://leetcode.cn/problems/parallel-courses-iii/
思路分析
代码实现
java
参加会议的最多员工数
题目链接
https://leetcode.cn/problems/maximum-employees-to-be-invited-to-a-meeting/
思路分析
代码实现
java
