代码随想录算法训练营四十八天|图论part06

冗余连接

题目描述

有一个图,它是一棵树,他是拥有 n 个节点(节点编号1到n)和 n - 1 条边的连通无环无向图(其实就是一个线形图),如图:

现在在这棵树上的基础上,添加一条边(依然是n个节点,但有n条边),使这个图变成了有环图,如图:

先请你找出冗余边,删除后,使该图可以重新变成一棵树。

输入描述

第一行包含一个整数 N,表示图的节点个数和边的个数。

后续 N 行,每行包含两个整数 s 和 t,表示图中 s 和 t 之间有一条边。

输出描述

输出一条可以删除的边。如果有多个答案,请删除标准输入中最后出现的那条边。

输入示例

3

1 2

2 3

1 3

输出示例

1 3

提示信息

图中的 1 2,2 3,1 3 等三条边在删除后都能使原图变为一棵合法的树。但是 1 3 由于是标准输入里最后出现的那条边,所以输出结果为 1 3。

数据范围:

1 <= N <= 1000.

本题是并查集的简单应用。每加入一条边,如果这条边的两个端点的根是一样的,说明这条边就是冗余边,把这条边输出即可。

代码如下:

java 复制代码
import java.util.*;

public class Main{
    private static int[] father;
    public static void main(String[] args){
        Scanner in=new Scanner(System.in);
        int n=in.nextInt();
        father=new int[n+1];
        init(father,n);
        for(int i=0;i<n;i++){
            int u=in.nextInt();
            int v=in.nextInt();
            join(u,v);
        }
    }
    public static void init(int[] array,int n){
        for(int i=1;i<=n;i++){
            array[i]=i;
        }
    }
    public static int find(int u){
        if(u==father[u])return u;
        else return find(father[u]);
    }
    public static void join(int u,int v){
        int a=u;
        int b=v;
        u=find(u);
        v=find(v);
        if(u==v){
            System.out.println(a+" "+b);
            return;
        }
        father[v]=u;
    }
}

冗余连接II

题目描述

有一种有向树,该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。有向树拥有 n 个节点和 n - 1 条边。如图:

现在有一个有向图,有向图是在有向树中的两个没有直接链接的节点中间添加一条有向边。如图:

输入一个有向图,该图由一个有着 n 个节点(节点编号 从 1 到 n),n 条边,请返回一条可以删除的边,使得删除该条边之后该有向图可以被当作一颗有向树。

输入描述

第一行输入一个整数 N,表示有向图中节点和边的个数。

后续 N 行,每行输入两个整数 s 和 t,代表这是 s 节点连接并指向 t 节点的单向边

输出描述

输出一条可以删除的边,若有多条边可以删除,请输出标准输入中最后出现的一条边。

输入示例

3

1 2

1 3

2 3

输出示例

2 3

提示信息

在删除 2 3 后有向图可以变为一棵合法的有向树,所以输出 2 3

数据范围:

1 <= N <= 1000

思路:

1.首先统计入度为2的节点,指向这个节点的两条有向边一定有一条是多余的。

java 复制代码
int[] indegree = new int[n + 1];
for (int i = 0; i < n; i++) {
     int u = in.nextInt();
     int v = in.nextInt();
     edges.add(new int[] {u, v});
     indegree[v]++;
}

2.如果有入度为2的节点,得到指向它的两条边。先看后输入的那条边,如果把这条边删掉之后,树就变为合法的有向树,那么直接输出这条边;如果树依旧不是合法的有向树,说明我们要删掉的就是另一条边,输出另一条边即可。

java 复制代码
for (int i = 0; i < edges.size(); i++) {
            if (indegree[edges.get(i)[1]] == 2) {
                list.add(edges.get(i));
            }
        }
        if (list.size()>0) {
            edges.remove(list.get(1));
            if (isLegal(edges) == true) {
                System.out.println(list.get(1)[0] + " " + list.get(1)[1]);
            } else {
                System.out.println(list.get(0)[0] + " " + list.get(0)[1]);
            }
        }
        else Circle(edges);

3.如果没有入度为2的节点,说明这时图中有环,那么这就和上面冗余连接是一样的情况,只需要删掉那条冗余边即可。

java 复制代码
public static void Circle(List<int[]> edges){
        for(int i=0;i<edges.size();i++){
            if (isSame(edges.get(i)[0], edges.get(i)[1]) == true) 
                System.out.println(edges.get(i)[0]+" "+ edges.get(i)[1]);
            join(edges.get(i)[0], edges.get(i)[1]);
        }
    }

整体代码如下:

java 复制代码
import java.util.*;
 
public class Main {
    private static int[] father;
    private static List<int[]> edges = new ArrayList<>();
    private static List<int[]> list = new ArrayList<>();
 
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        father = new int[n + 1];
        init(father, n);
        int[] indegree = new int[n + 1];
        for (int i = 0; i < n; i++) {
            int u = in.nextInt();
            int v = in.nextInt();
            edges.add(new int[] {u, v});
            indegree[v]++;
        }
        for (int i = 0; i < edges.size(); i++) {
            if (indegree[edges.get(i)[1]] == 2) {
                list.add(edges.get(i));
            }
        }
        if (list.size()>0) {
            edges.remove(list.get(1));
            if (isLegal(edges) == true) {
                System.out.println(list.get(1)[0] + " " + list.get(1)[1]);
            } else {
                System.out.println(list.get(0)[0] + " " + list.get(0)[1]);
            }
        }
        else Circle(edges);
    }
    public static void Circle(List<int[]> edges){
        for(int i=0;i<edges.size();i++){
            if (isSame(edges.get(i)[0], edges.get(i)[1]) == true) System.out.println(edges.get(i)[0]+" "+ edges.get(i)[1]);
            join(edges.get(i)[0], edges.get(i)[1]);
        }
    }
    public static void init(int[] array, int n) {
        for (int i = 1; i <= n; i++) {
            array[i] = i;
        }
    }
 
    public static boolean isLegal(List<int[]> edges) {
        for (int i = 0; i < edges.size(); i++) {
            if (isSame(edges.get(i)[0], edges.get(i)[1]) == true) return false;
            join(edges.get(i)[0], edges.get(i)[1]);
        }
        return true;
    }
 
    public static int find(int u) {
        if (u == father[u]) return u;
        else return father[u] = find(father[u]);
    }
 
    public static void join(int u, int v) {
        u = find(u);
        v = find(v);
        if (u == v) return;
        father[v] = u;
    }
 
    public static boolean isSame(int u, int v) {
        u = find(u);
        v = find(v);
        return u == v;
    }
}