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

相关文章
|
3月前
|
算法
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
|
7天前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
45 8
|
7天前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
33 7
|
1月前
|
搜索推荐 Shell
解析排序算法:十大排序方法的工作原理与性能比较
解析排序算法:十大排序方法的工作原理与性能比较
48 9
|
1月前
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
23 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
1月前
|
算法
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
30 0
|
1月前
|
存储 算法 搜索推荐
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
68 0
|
3月前
|
搜索推荐 算法 Java
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
该博客文章通过UML类图和Java源码示例,展示了如何使用适配器模式将QuickSort类和BinarySearch类的排序和查找功能适配到DataOperation接口中,实现算法的解耦和复用。
37 1
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
|
2月前
|
资源调度 算法
基于迭代扩展卡尔曼滤波算法的倒立摆控制系统matlab仿真
本课题研究基于迭代扩展卡尔曼滤波算法的倒立摆控制系统,并对比UKF、EKF、迭代UKF和迭代EKF的控制效果。倒立摆作为典型的非线性系统,适用于评估不同滤波方法的性能。UKF采用无迹变换逼近非线性函数,避免了EKF中的截断误差;EKF则通过泰勒级数展开近似非线性函数;迭代EKF和迭代UKF通过多次迭代提高状态估计精度。系统使用MATLAB 2022a进行仿真和分析,结果显示UKF和迭代UKF在非线性强的系统中表现更佳,但计算复杂度较高;EKF和迭代EKF则更适合维数较高或计算受限的场景。
|
3月前
|
算法 搜索推荐 Java
算法实战:手写归并排序,让复杂排序变简单!
归并排序是一种基于“分治法”的经典算法,通过递归分割和合并数组,实现O(n log n)的高效排序。本文将通过Java手写代码,详细讲解归并排序的原理及实现,帮助你快速掌握这一实用算法。
41 0