当前位置:   article > 正文

代码随想录Day70(图论Part06)

代码随想录Day70(图论Part06)

108.冗余连接

题目:108. 冗余连接 (kamacoder.com)

思路:每次更新输出的边,来保证删除的是输入中最后出现的那条边。关键是,我要知道哪条边可以删除,而且是在join的时候就判断

尝试(难得AC)
  1. import java.util.Scanner;
  2. public class Main {
  3. private static int[] father;
  4. private static int s1 =0;
  5. private static int t1 =0;
  6. public static void main(String[] args) {
  7. Scanner scanner = new Scanner(System.in);
  8. int n = scanner.nextInt(); // 节点数量
  9. // 初始化并查集
  10. father = new int[n + 1];
  11. init(n);
  12. // 读取边并构建并查集
  13. for (int i = 0; i < n; i++) {
  14. int s = scanner.nextInt();
  15. int t = scanner.nextInt();
  16. join(s, t);
  17. }
  18. System.out.println(s1+" "+t1);
  19. }
  20. // 并查集初始化
  21. private static void init(int n) {
  22. for (int i = 1; i <= n; i++) {
  23. father[i] = i;
  24. }
  25. }
  26. // 并查集里寻根的过程
  27. private static int find(int u) {
  28. if (u != father[u]) {
  29. father[u] = find(father[u]);
  30. }
  31. return father[u];
  32. }
  33. // 判断 u 和 v 是否找到同一个根
  34. private static boolean isSame(int u, int v) {
  35. return find(u) == find(v);
  36. }
  37. // 将 v -> u 这条边加入并查集
  38. private static void join(int u, int v) {
  39. int rootU = find(u);
  40. int rootV = find(v);
  41. if (rootU != rootV) {
  42. father[rootV] = rootU;
  43. }else{
  44. s1 = u;
  45. t1 = v;
  46. }
  47. }
  48. }
小结

基于【寻找存在的路径】代码改造,如果发现输入的(s,t)指向同一个根,说明是冗余连接,通过s1,t1每次join的时候更新

109.冗余连接||

题目:109. 冗余连接II (kamacoder.com)

思路:按照题目的意思,至少有一条边是可以删除的,我想通过fa数组,找到fa中最少被指向的元素,删掉该边,或者是说,输出该边

在遍历father数组时,还需要记录是哪条边,或许可以用set来存储

尝试(标题4)
  1. import java.util.*;
  2. class Main {
  3. public static int[] father;
  4. public static void main(String[] args) {
  5. Scanner scanner = new Scanner(System.in);
  6. int n = scanner.nextInt();
  7. father = new int[n + 1];
  8. init(n);
  9. for (int i = 0; i < n; i++) { // 修正循环范围
  10. int s = scanner.nextInt();
  11. int t = scanner.nextInt();
  12. join(s, t);
  13. }
  14. int[] count = new int[n + 1];
  15. Map<Integer, Integer> map = new HashMap<>(); // 使用 Map
  16. for (int i = 1; i <= n; i++) { // 修正循环范围
  17. int root = find(i); // 确保父节点是根节点
  18. count[root]++;
  19. map.put(i, root);
  20. }
  21. for (int i = 1; i <= n; i++) { // 修正循环范围
  22. if (count[i] == 1) {
  23. System.out.println(map.get(i) + " " + i);
  24. }
  25. }
  26. }
  27. public static void init(int n) {
  28. for (int i = 1; i <= n; i++) {
  29. father[i] = i;
  30. }
  31. }
  32. public static int find(int u) {
  33. if (u != father[u]) {
  34. father[u] = find(father[u]);
  35. }
  36. return father[u];
  37. }
  38. public static void join(int u, int v) {
  39. int rootU = find(u);
  40. int rootV = find(v);
  41. if (rootU != rootV) {
  42. father[rootU] = rootV;
  43. }
  44. }
  45. }
答案
  1. import java.util.*;
  2. public class Main {
  3. public static int n;
  4. public static int[] father;
  5. public static int[] inDegree;
  6. public static void main(String[] args) {
  7. Scanner scanner = new Scanner(System.in);
  8. n = scanner.nextInt();
  9. father = new int[n + 1];
  10. inDegree = new int[n + 1];
  11. List<int[]> edges = new ArrayList<>();
  12. for (int i = 0; i < n; i++) {
  13. int s = scanner.nextInt();
  14. int t = scanner.nextInt();
  15. inDegree[t]++;
  16. edges.add(new int[]{s, t});
  17. }
  18. List<Integer> vec = new ArrayList<>(); // 记录入度为2的边(如果有的话就两条边)
  19. // 找入度为2的节点所对应的边,注意要倒序,因为优先删除最后出现的一条边
  20. for (int i = n - 1; i >= 0; i--) {
  21. if (inDegree[edges.get(i)[1]] == 2) {
  22. vec.add(i);
  23. }
  24. }
  25. if (!vec.isEmpty()) {
  26. // 放在vec里的边已经按照倒序放的,所以这里就优先删vec.get(0)这条边
  27. if (isTreeAfterRemoveEdge(edges, vec.get(0))) {
  28. System.out.println(edges.get(vec.get(0))[0] + " " + edges.get(vec.get(0))[1]);
  29. } else {
  30. System.out.println(edges.get(vec.get(1))[0] + " " + edges.get(vec.get(1))[1]);
  31. }
  32. return;
  33. }
  34. // 处理情况三
  35. // 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
  36. getRemoveEdge(edges);
  37. }
  38. // 并查集初始化
  39. public static void init() {
  40. for (int i = 1; i <= n; ++i) {
  41. father[i] = i;
  42. }
  43. }
  44. // 并查集里寻根的过程
  45. public static int find(int u) {
  46. return u == father[u] ? u : (father[u] = find(father[u]));
  47. }
  48. // 将v->u 这条边加入并查集
  49. public static void join(int u, int v) {
  50. u = find(u);
  51. v = find(v);
  52. if (u == v) return;
  53. father[v] = u;
  54. }
  55. // 判断 u 和 v是否找到同一个根
  56. public static boolean same(int u, int v) {
  57. u = find(u);
  58. v = find(v);
  59. return u == v;
  60. }
  61. // 在有向图里找到删除的那条边,使其变成树
  62. public static void getRemoveEdge(List<int[]> edges) {
  63. init(); // 初始化并查集
  64. for (int i = 0; i < n; i++) { // 遍历所有的边
  65. if (same(edges.get(i)[0], edges.get(i)[1])) { // 构成有向环了,就是要删除的边
  66. System.out.println(edges.get(i)[0] + " " + edges.get(i)[1]);
  67. return;
  68. } else {
  69. join(edges.get(i)[0], edges.get(i)[1]);
  70. }
  71. }
  72. }
  73. // 删一条边之后判断是不是树
  74. public static boolean isTreeAfterRemoveEdge(List<int[]> edges, int deleteEdge) {
  75. init(); // 初始化并查集
  76. for (int i = 0; i < n; i++) {
  77. if (i == deleteEdge) continue;
  78. if (same(edges.get(i)[0], edges.get(i)[1])) { // 构成有向环了,一定不是树
  79. return false;
  80. }
  81. join(edges.get(i)[0], edges.get(i)[1]);
  82. }
  83. return true;
  84. }
  85. }
小结

要考虑的情况有三种

  • 有入度为2的节点
    • 删除前,先判断删除后,本图能否成为有向树
    • 删哪个都可以时,就选择顺序靠后的删除

  • 没有入度为2的节点
    • 图中有环,删掉构成环的边

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/804916
推荐阅读
相关标签
  

闽ICP备14008679号