class060 拓扑排序的扩展技巧【算法】

class060 拓扑排序的扩展技巧【算法】

算法讲解060【必备】拓扑排序的扩展技巧

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

相关推荐

  1. 算法拓扑排序

    2023-12-09 20:26:04       18 阅读
  2. C++算法拓扑排序原理及应用

    2023-12-09 20:26:04       4 阅读
  3. C语言-算法-拓扑排序

    2023-12-09 20:26:04       34 阅读
  4. 算法——图论:拓扑排序

    2023-12-09 20:26:04       18 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-09 20:26:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-09 20:26:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-09 20:26:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-09 20:26:04       18 阅读

热门阅读

  1. 使用es256算法生成jwt

    2023-12-09 20:26:04       44 阅读
  2. Win32 枚举指定进程所有子窗口

    2023-12-09 20:26:04       34 阅读
  3. STM32的几个深入功能

    2023-12-09 20:26:04       31 阅读
  4. C# Bin、XML、Json的序列化和反序列化

    2023-12-09 20:26:04       36 阅读
  5. k8s中部署基于nfs的StorageClass

    2023-12-09 20:26:04       40 阅读
  6. 【PyTorch】计算设备

    2023-12-09 20:26:04       42 阅读
  7. 做题笔记:SQL Sever 方式做牛客SQL的题目--SQL156

    2023-12-09 20:26:04       33 阅读
  8. Nacos和Eureka冲突问题原因分析

    2023-12-09 20:26:04       37 阅读
  9. LeetCodehot100

    2023-12-09 20:26:04       34 阅读
  10. #HarmonyOS:基础语法

    2023-12-09 20:26:04       43 阅读