class060 拓扑排序的扩展技巧【算法】
2023-12-7 22:23:02
code1 P4017 最大食物链计数
// 最大食物链计数
// a -> b,代表a在食物链中被b捕食
// 给定一个有向无环图,返回
// 这个图中从最初级动物到最顶级捕食者的食物链有几条
// 测试链接 : https://www.luogu.com.cn/problem/P4017
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下所有代码,把主类名改成Main,可以直接通过
package class060; // 最大食物链计数 // a -> b,代表a在食物链中被b捕食 // 给定一个有向无环图,返回 // 这个图中从最初级动物到最顶级捕食者的食物链有几条 // 测试链接 : https://www.luogu.com.cn/problem/P4017 // 请同学们务必参考如下代码中关于输入、输出的处理 // 这是输入输出处理效率很高的写法 // 提交以下所有代码,把主类名改成Main,可以直接通过 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.StreamTokenizer; import java.util.Arrays; public class Code01_FoodLines { 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); } public static void addEdge(int u, int v) { next[cnt] = head[u]; to[cnt] = v; 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, u, v; i < m; i++) { in.nextToken(); u = (int) in.nval; in.nextToken(); v = (int) in.nval; 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++]; if (head[u] == 0) { // 当前的u节点不再有后续邻居了 ans = (ans + lines[u]) % MOD; } else { for (int ei = head[u], v; ei > 0; ei = next[ei]) { // u -> v v = to[ei]; lines[v] = (lines[v] + lines[u]) % MOD; if (--indegree[v] == 0) { queue[r++] = v; } } } } return ans; } }
code2 851. 喧闹和富有
// 喧闹和富有
// 从 0 到 n - 1 编号,其中每个人都有不同数目的钱,以及不同程度的安静值
// 给你一个数组richer,其中richer[i] = [ai, bi] 表示
// person ai 比 person bi 更有钱
// 还有一个整数数组 quiet ,其中 quiet[i] 是 person i 的安静值
// richer 中所给出的数据 逻辑自洽
// 也就是说,在 person x 比 person y 更有钱的同时,不会出现
// person y 比 person x 更有钱的情况
// 现在,返回一个整数数组 answer 作为答案,其中 answer[x] = y 的前提是,
// 在所有拥有的钱肯定不少于 person x 的人中,
// person y 是最安静的人(也就是安静值 quiet[y] 最小的人)。
// 测试链接 : https://leetcode.cn/problems/loud-and-rich/
package class060; import java.util.ArrayList; // 喧闹和富有 // 从 0 到 n - 1 编号,其中每个人都有不同数目的钱,以及不同程度的安静值 // 给你一个数组richer,其中richer[i] = [ai, bi] 表示 // person ai 比 person bi 更有钱 // 还有一个整数数组 quiet ,其中 quiet[i] 是 person i 的安静值 // richer 中所给出的数据 逻辑自洽 // 也就是说,在 person x 比 person y 更有钱的同时,不会出现 // person y 比 person x 更有钱的情况 // 现在,返回一个整数数组 answer 作为答案,其中 answer[x] = y 的前提是, // 在所有拥有的钱肯定不少于 person x 的人中, // person y 是最安静的人(也就是安静值 quiet[y] 最小的人)。 // 测试链接 : https://leetcode.cn/problems/loud-and-rich/ public class Code02_LoudAndRich { public static int[] loudAndRich(int[][] richer, int[] quiet) { int n = quiet.length; ArrayList<ArrayList<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[cur]] < quiet[ans[next]] ) { ans[next] = ans[cur]; } if (--indegree[next] == 0) { queue[r++] = next; } } } return ans; } }
code3 2050. 并行课程 III
// 并行课程 III
// 给你一个整数 n ,表示有 n 节课,课程编号从 1 到 n
// 同时给你一个二维整数数组 relations ,
// 其中 relations[j] = [prevCoursej, nextCoursej]
// 表示课程 prevCoursej 必须在课程 nextCoursej 之前 完成(先修课的关系)
// 同时给你一个下标从 0 开始的整数数组 time
// 其中 time[i] 表示完成第 (i+1) 门课程需要花费的 月份 数。
// 请你根据以下规则算出完成所有课程所需要的 最少 月份数:
// 如果一门课的所有先修课都已经完成,你可以在 任意 时间开始这门课程。
// 你可以 同时 上 任意门课程 。请你返回完成所有课程所需要的 最少 月份数。
// 注意:测试数据保证一定可以完成所有课程(也就是先修课的关系构成一个有向无环图)
// 测试链接 : https://leetcode.cn/problems/parallel-courses-iii/
package class060; import java.util.ArrayList; // 并行课程 III // 给你一个整数 n ,表示有 n 节课,课程编号从 1 到 n // 同时给你一个二维整数数组 relations , // 其中 relations[j] = [prevCoursej, nextCoursej] // 表示课程 prevCoursej 必须在课程 nextCoursej 之前 完成(先修课的关系) // 同时给你一个下标从 0 开始的整数数组 time // 其中 time[i] 表示完成第 (i+1) 门课程需要花费的 月份 数。 // 请你根据以下规则算出完成所有课程所需要的 最少 月份数: // 如果一门课的所有先修课都已经完成,你可以在 任意 时间开始这门课程。 // 你可以 同时 上 任意门课程 。请你返回完成所有课程所需要的 最少 月份数。 // 注意:测试数据保证一定可以完成所有课程(也就是先修课的关系构成一个有向无环图) // 测试链接 : https://leetcode.cn/problems/parallel-courses-iii/ public class Code03_ParallelCoursesIII { public static int minimumTime(int n, int[][] relations, int[] time) { // 点 : 1....n ArrayList<ArrayList<Integer>> graph = new ArrayList<>(); for (int i = 0; i <= n; i++) { graph.add(new ArrayList<>()); } int[] indegree = new int[n + 1]; for (int[] edge : relations) { graph.get(edge[0]).add(edge[1]); indegree[edge[1]]++; } int[] queue = new int[n]; int l = 0; int r = 0; for (int i = 1; i <= n; i++) { if (indegree[i] == 0) { queue[r++] = i; } } int[] cost = new int[n + 1]; int ans = 0; while (l < r) { int cur = queue[l++]; // 1 : time[0] // x : time[x-1] cost[cur] += time[cur - 1]; ans = Math.max(ans, cost[cur]); for (int next : graph.get(cur)) { cost[next] = Math.max(cost[next], cost[cur]); if (--indegree[next] == 0) { queue[r++] = next; } } } return ans; } }
code4 2127. 参加会议的最多员工数
// 参加会议的最多员工数
// 一个公司准备组织一场会议,邀请名单上有 n 位员工
// 公司准备了一张 圆形 的桌子,可以坐下 任意数目 的员工
// 员工编号为 0 到 n - 1 。每位员工都有一位 喜欢 的员工
// 每位员工 当且仅当 他被安排在喜欢员工的旁边,他才会参加会议
// 每位员工喜欢的员工 不会 是他自己。给你一个下标从 0 开始的整数数组 favorite
// 其中 favorite[i] 表示第 i 位员工喜欢的员工。请你返回参加会议的 最多员工数目
// 测试链接 : https://leetcode.cn/problems/maximum-employees-to-be-invited-to-a-meeting/
package class060; // 参加会议的最多员工数 // 一个公司准备组织一场会议,邀请名单上有 n 位员工 // 公司准备了一张 圆形 的桌子,可以坐下 任意数目 的员工 // 员工编号为 0 到 n - 1 。每位员工都有一位 喜欢 的员工 // 每位员工 当且仅当 他被安排在喜欢员工的旁边,他才会参加会议 // 每位员工喜欢的员工 不会 是他自己。给你一个下标从 0 开始的整数数组 favorite // 其中 favorite[i] 表示第 i 位员工喜欢的员工。请你返回参加会议的 最多员工数目 // 测试链接 : https://leetcode.cn/problems/maximum-employees-to-be-invited-to-a-meeting/ public class Code04_MaximumEmployeesToBeInvitedToAMeeting { public static int maximumInvitations(int[] favorite) { // 图 : favorite[a] = b : a -> b int n = favorite.length; int[] indegree = new int[n]; for (int i = 0; i < n; i++) { indegree[favorite[i]]++; } 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; } } // deep[i] : 不包括i在内,i之前的最长链的长度 int[] deep = new int[n]; while (l < r) { int cur = queue[l++]; int next = favorite[cur]; deep[next] = Math.max(deep[next], deep[cur] + 1); if (--indegree[next] == 0) { queue[r++] = next; } } // 目前图中的点,不在环上的点,都删除了! indegree[i] == 0 // 可能性1 : 所有小环(中心个数 == 2),算上中心点 + 延伸点,总个数 int sumOfSmallRings = 0; // 可能性2 : 所有大环(中心个数 > 2),只算中心点,最大环的中心点个数 int bigRings = 0; for (int i = 0; i < n; i++) { // 只关心的环! if (indegree[i] > 0) { int ringSize = 1; indegree[i] = 0; for (int j = favorite[i]; j != i; j = favorite[j]) { ringSize++; indegree[j] = 0; } if (ringSize == 2) { sumOfSmallRings += 2 + deep[i] + deep[favorite[i]]; } else { bigRings = Math.max(bigRings, ringSize); } } } return Math.max(sumOfSmallRings, bigRings); } }
2023-12-8 11:42:29