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

简介: 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

相关文章
|
2月前
|
算法
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
|
29天前
|
资源调度 算法
基于迭代扩展卡尔曼滤波算法的倒立摆控制系统matlab仿真
本课题研究基于迭代扩展卡尔曼滤波算法的倒立摆控制系统,并对比UKF、EKF、迭代UKF和迭代EKF的控制效果。倒立摆作为典型的非线性系统,适用于评估不同滤波方法的性能。UKF采用无迹变换逼近非线性函数,避免了EKF中的截断误差;EKF则通过泰勒级数展开近似非线性函数;迭代EKF和迭代UKF通过多次迭代提高状态估计精度。系统使用MATLAB 2022a进行仿真和分析,结果显示UKF和迭代UKF在非线性强的系统中表现更佳,但计算复杂度较高;EKF和迭代EKF则更适合维数较高或计算受限的场景。
|
2月前
|
搜索推荐 算法 Java
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
该博客文章通过UML类图和Java源码示例,展示了如何使用适配器模式将QuickSort类和BinarySearch类的排序和查找功能适配到DataOperation接口中,实现算法的解耦和复用。
22 1
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
|
2月前
|
算法 搜索推荐 Java
算法实战:手写归并排序,让复杂排序变简单!
归并排序是一种基于“分治法”的经典算法,通过递归分割和合并数组,实现O(n log n)的高效排序。本文将通过Java手写代码,详细讲解归并排序的原理及实现,帮助你快速掌握这一实用算法。
38 0
|
2月前
|
算法 关系型数据库 MySQL
揭秘MySQL中的版本号排序:这个超级算法将颠覆你的排序世界!
【8月更文挑战第8天】在软件开发与数据管理中,正确排序版本号对软件更新及数据分析至关重要。因MySQL默认按字符串排序版本号,可能出现&#39;1.20.0&#39;在&#39;1.10.0&#39;之前的不合理情况。解决办法是将版本号各部分转换为整数后排序。例如,使用`SUBSTRING_INDEX`和`CAST`函数从`software`表的`version`字段提取并转换版本号,再按这些整数排序。这种方法可确保版本号按逻辑正确排序,适用于&#39;major.minor.patch&#39;格式的版本号。对于更复杂格式,需调整处理逻辑。掌握此技巧可有效应对版本号排序需求。
104 3
|
2月前
|
算法 搜索推荐
算法设计 (分治法应用实验报告)基于分治法的合并排序、快速排序、最近对问题
这篇文章是关于分治法应用的实验报告,详细介绍了如何利用分治法实现合并排序和快速排序算法,并探讨了使用分治法解决二维平面上的最近对问题的方法,包括伪代码、源代码实现及时间效率分析,并附有运行结果和小结。
|
2月前
|
存储 算法 Java
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
20 0
|
3月前
|
存储 算法 搜索推荐
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
【7月更文挑战第12天】归并排序是高效稳定的排序算法,采用分治策略。Python 实现包括递归地分割数组及合并已排序部分。示例代码展示了如何将 `[12, 11, 13, 5, 6]` 分割并归并成有序数组 `[5, 6, 11, 12, 13]`。虽然 $O(n log n)$ 时间复杂度优秀,但需额外空间,适合大规模数据排序。对于小规模数据,可考虑其他算法。**
67 4
|
4月前
|
算法 Java 调度
Java数据结构与算法:拓扑排序
Java数据结构与算法:拓扑排序
|
4月前
|
算法 搜索推荐 C++
C++之STL常用算法(遍历、查找、排序、拷贝、替换、算数生成、集合)
C++之STL常用算法(遍历、查找、排序、拷贝、替换、算数生成、集合)
下一篇
无影云桌面