Blue Bridge Cup JAVA-22 Judgment of bipartite graph and its maximum matching template (JAVA implementation)

Posted by FarhanKhalaf on Tue, 08 Feb 2022 17:52:25 +0100

Personal blog
www.tothefor.com
Summary of review knowledge points of Blue Bridge Cup

I feel shallow when I get it on paper. I absolutely know that I have to practice it. Although the road is endless and faraway, I still want to pursue the truth in the world. Knowledge is precipitated by the giants of history. Don't always think that you can learn it quickly. Take more time to see it, and you may find something different. What you can quickly learn and feel simple is the same for others. There are people outside, and there are days outside. When the effort reaches a certain level, luck will meet you by chance.

catalogue

Tip: unless otherwise specified, n represents the number of points and m represents the number of edges.

Foresee

Bipartite graph: in short, the vertex set can be divided into two disjoint subsets, and the two vertices attached to each edge in the graph belong to the two disjoint subsets, and the vertices in the two subsets are not adjacent (connected). As shown in the figure:

Odd ring: the number of points in a ring is odd (or the length of edges is even). As shown below:

Properties: bipartite graph if and only if the graph does not contain odd rings. (bipartite graph judgment conditions)

Dyeing method

Time complexity: O(n+m).

It is often used to judge whether a graph is a bipartite graph.

Meaning:

Given an undirected graph with n points and m edges, there may be multiple edges and self rings in the graph. Now judge whether this graph is a bipartite graph.

Enter two integers n and m.

The next m lines are two integers u and v respectively, indicating that there is an edge between u point and v point.

If it is a bipartite graph, output "YES", otherwise output "NO".

1≤n,m≤105

test data

Input:

4 4
1 3
1 4
2 3
2 4

Output:

YES

DFS implementation

Use 1 and 2 to distinguish different colors, and 0 means not stained.

Traverse all points (because it is not described as a connected graph) and dye the non stained points (dfs).

import java.io.*;
import java.math.BigInteger;
import java.util.*;


/**
 * @Author DragonOne
 * @Date 2021/12/5 21:27
 * @Ink memory www.tothefor.com com
 */
public class Main {
    public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    public static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    public static Scanner sc = new Scanner(System.in);

    public static int maxd = 2000+7;
    public static int INF = 0x3f3f3f3f;
    public static int mod = 998244353;

    public static int[] head = new int[maxd];
    public static int[] edgePre = new int[maxd];
    public static int[] edgeTo = new int[maxd];
    public static int[] color = new int[maxd]; //No. 0 indicates no staining, No. 1 indicates staining as one type, and No. 2 indicates another type
    public static int node = 0;

    public static void init(int n,int m){
        for(int i=1;i<=n;++i){
            head[i]=-1;
            color[i]=0;
        }
        node=0;
    }
    public static void add_edge(int a,int b){
        edgeTo[node]=b;
        edgePre[node]=head[a];
        head[a]=node++;
    }
    public static boolean dfs(int u,int c){ //true is a bipartite graph and false is a non bipartite graph
        color[u] = c;
        for(int i=head[u];i!=-1;i=edgePre[i]){ //Stain a point that has an edge with the current point and dye it to the opposite color
            int v = edgeTo[i];
            if(color[v]==0){ //If it is not dyed, dye it with different colors
                if(!dfs(v,3-c)){ //3-c represents the colors different from c. because there are only 1 and 2 colors here, subtracting the current color by 3 is the opposite color
                    return false; //Dyeing failure
                }
            }else if(color[v]==c){ //If it has been dyed, but it is the same color as the previous point
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) throws Exception {

        int n = nextInt();
        int m = nextInt();
        init(n,m);
        while(m-->0){
            int a = nextInt();
            int b = nextInt();
            add_edge(a,b); //Undirected graph
            add_edge(b,a);
        }
        boolean flag = true; //It is used to mark whether the final result is a bipartite graph. It is a bipartite graph by default
        for(int i=1;i<=n;++i){//Traversing all points is to prevent the graph from being disconnected. If it is a connected graph, it is not necessary to traverse all points
            if(color[i]==0){ //Not stained
                if(!dfs(i,1)){ //If it's not a bipartite graph. Here, the initial dyeing is 1 or 2, which can be customized
                    flag=false;
                    break;
                }
            }
        }
        if(flag) System.out.println("YES"); //It's a bipartite graph
        else System.out.println("NO");

        closeAll();
    }

    public static void cinInit(){
        cin.wordChars('a', 'z');
        cin.wordChars('A', 'Z');
        cin.wordChars(128 + 32, 255);
        cin.whitespaceChars(0, ' ');
        cin.commentChar('/');
        cin.quoteChar('"');
        cin.quoteChar('\'');
        cin.parseNumbers(); //Can be used alone to restore numbers
    }

    public static int nextInt() throws Exception{
        cin.nextToken();
        return (int) cin.nval;
    }
    public static long nextLong() throws Exception{
        cin.nextToken();
        return (long) cin.nval;
    }
    public static double nextDouble() throws Exception{
        cin.nextToken();
        return cin.nval;
    }
    public static String nextString() throws Exception{
        cin.nextToken();
        return cin.sval;
    }
    public static void closeAll() throws Exception {
        cout.close();
        in.close();
        out.close();
    }

}

BFS implementation

  • Colors 1 and 2 represent different colors, and 0 represents not dyed. Traverse all points (because it is not described as a connected graph) and dye the non stained points (bfs).

  • Store < point number, color > in the queue.

Specific ideas:

  1. Queue initialization: queue the ith point. The default color can be 1 or 2.
  1. When the queue is not empty (while()), get the first element of the queue every time and traverse the points with edges to the first element.
  1. If the points of adjacent edges are not stained, they will be dyed with the opposite color to the first element and added to the queue.

    If the point of the adjacent edge has been dyed and the color is the same as that of the first element, false is returned.

import java.io.*;
import java.math.BigInteger;
import java.util.*;


/**
 * @Author DragonOne
 * @Date 2021/12/5 21:27
 * @Ink memory www.tothefor.com com
 */
public class Main {
    public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    public static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    public static Scanner sc = new Scanner(System.in);

    public static int maxd = 2000+7;
    public static int INF = 0x3f3f3f3f;
    public static int mod = 998244353;

    public static int[] head = new int[maxd];
    public static int[] edgePre = new int[maxd];
    public static int[] edgeTo = new int[maxd];
    public static int[] color = new int[maxd]; //0 indicates that it is not stained, 1 indicates that it is stained as one type, and 2 indicates another type
    public static int node = 0;

    public static class POT{
        private int point; //Point number
        private int color; //Color number

        public POT() {
        }
        public POT(int point, int color) {
            this.point = point;
            this.color = color;
        }
    }

    public static void init(int n,int m){
        for(int i=1;i<=n;++i){
            head[i]=-1;
            color[i]=0;
        }
        node=0;
    }
    public static void add_edge(int a,int b){
        edgeTo[node]=b;
        edgePre[node]=head[a];
        head[a]=node++;
    }
    public static boolean bfs(int u,int c){ //true is a bipartite graph and false is a non bipartite graph
        Queue<POT> q = new ArrayDeque<>();
        q.add(new POT(u,c)); //Join the team
        color[u]=c; //Staining is c
        while(!q.isEmpty()){
            POT mid = q.poll();
            int v = mid.point;
            int cc = mid.color;
            for(int i=head[v];i!=-1;i=edgePre[i]){ //Traverses points that have edges to the current point
                int vv = edgeTo[i];
                if(color[vv]==0){ //If not stained
                    color[vv]=3-cc;
                    q.add(new POT(vv,3-cc));
                }else if(color[vv]==cc){ //If it has been dyed, but the dyeing situation is the same as that of the previous point
                    return false;
                }
            }
        }
        return true;
    }

    public static void main(String[] args) throws Exception {

        int n = nextInt();
        int m = nextInt();
        init(n,m);
        while(m-->0){
            int a = nextInt();
            int b = nextInt();
            add_edge(a,b); //Undirected graph
            add_edge(b,a);
        }
        boolean flag = true; //It is used to mark whether the final result is a bipartite graph. It is a bipartite graph by default
        for(int i=1;i<=n;++i){ //Traversing all points is to prevent the graph from being disconnected. If it is a connected graph, it is not necessary to traverse all points
            if(color[i]==0){ //Not stained
                if(!bfs(i,1)){ //If it's not a bipartite graph
                    flag=false;
                    break;
                }
            }
        }
        if(flag) System.out.println("YES"); //It's a bipartite graph
        else System.out.println("NO");

        closeAll();
    }

    public static void cinInit(){
        cin.wordChars('a', 'z');
        cin.wordChars('A', 'Z');
        cin.wordChars(128 + 32, 255);
        cin.whitespaceChars(0, ' ');
        cin.commentChar('/');
        cin.quoteChar('"');
        cin.quoteChar('\'');
        cin.parseNumbers(); //Can be used alone to restore numbers
    }

    public static int nextInt() throws Exception{
        cin.nextToken();
        return (int) cin.nval;
    }
    public static long nextLong() throws Exception{
        cin.nextToken();
        return (long) cin.nval;
    }
    public static double nextDouble() throws Exception{
        cin.nextToken();
        return cin.nval;
    }
    public static String nextString() throws Exception{
        cin.nextToken();
        return cin.sval;
    }
    public static void closeAll() throws Exception {
        cout.close();
        in.close();
        out.close();
    }

}

Joint search set judgment bipartite graph

We know that if it is a bipartite graph, all adjacent points of each vertex in the graph should belong to the same set and not in the same set with the vertices. Therefore, we can use union search set to solve this problem. We traverse each vertex in the graph, merge all adjacent points of the current vertex, and judge whether there is an adjacent point in these adjacent points, which is already in the same set as the current vertex. If so, it indicates that it is not a bipartite graph.

import java.io.*;
import java.math.BigInteger;
import java.util.*;


/**
 * @Author DragonOne
 * @Date 2021/12/5 21:27
 * @Ink memory www.tothefor.com com
 */
public class Main {
    public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    public static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    public static Scanner sc = new Scanner(System.in);

    public static int maxd = 2000+7;
    public static int INF = 0x3f3f3f3f;
    public static int mod = 998244353;

    public static int[] head = new int[maxd];
    public static int[] edgePre = new int[maxd];
    public static int[] edgeTo = new int[maxd];
    public static int[] par = new int[maxd];
    public static int node = 0;

    public static int find(int x){
        if(x!=par[x]) par[x] = find(par[x]);
        return par[x];
    }
    public static void unite(int a,int b){
        int x = find(a);
        int y = find(b);
        if(x!=y){
            par[x]=y;
        }
    }

    public static void init(int n,int m){
        for(int i=1;i<=n;++i){
            head[i]=-1;
            par[i]=i;
        }
        node=0;
    }
    public static void add_edge(int a,int b){
        edgeTo[node]=b;
        edgePre[node]=head[a];
        head[a]=node++;
    }
    public static boolean bjc(int n){ //true is a bipartite graph and false is a non bipartite graph
        for(int i=1;i<=n;++i){
            int firstP = edgeTo[head[i]]; //Get the first adjacent contact of the current point i
            for(int j=head[i];j!=-1;j=edgePre[j]){ //Traverse all adjacent points of the current point i
                int v = edgeTo[j];
                if(find(i)==find(v)){ //If the adjacency point of i is in the same set as i
                    return false;
                }
                unite(firstP,v); //If the adjacency point of i is not in the same set as i, the adjacency point of i is merged with the adjacency point
            }
        }
        return true;

    }

    public static void main(String[] args) throws Exception {

        int n = nextInt();
        int m = nextInt();
        init(n,m);
        while(m-->0){
            int a = nextInt();
            int b = nextInt();
            add_edge(a,b); //Undirected graph
            add_edge(b,a);
        }

        if(bjc(n)) System.out.println("YES"); //It's a bipartite graph
        else System.out.println("NO");

        closeAll();
    }

    public static void cinInit(){
        cin.wordChars('a', 'z');
        cin.wordChars('A', 'Z');
        cin.wordChars(128 + 32, 255);
        cin.whitespaceChars(0, ' ');
        cin.commentChar('/');
        cin.quoteChar('"');
        cin.quoteChar('\'');
        cin.parseNumbers(); //Numbers can be restored separately
    }

    public static int nextInt() throws Exception{
        cin.nextToken();
        return (int) cin.nval;
    }
    public static long nextLong() throws Exception{
        cin.nextToken();
        return (long) cin.nval;
    }
    public static double nextDouble() throws Exception{
        cin.nextToken();
        return cin.nval;
    }
    public static String nextString() throws Exception{
        cin.nextToken();
        return cin.sval;
    }
    public static void closeAll() throws Exception {
        cout.close();
        in.close();
        out.close();
    }

}

hungarian algorithm

Time complexity: O(n*m), and the actual running time is generally much less than O(n*m).

It is often used to find the maximum match given a bipartite graph.

Maximum matching: two sets are regarded as two genders (male and female), and people (male and female) can be seen by clicking. The biggest match is how many couples can be found, provided that two people know each other (connected).

Meaning:

  • Enumerate male point sets, then the side storage is only from male to female. Because only all girls corresponding to boys will be found, not all boys corresponding to girls. (it's OK to start from the girls' point set, and others can be reversed.)
import java.io.*;
import java.math.BigInteger;
import java.util.*;


/**
 * @Author DragonOne
 * @Date 2021/12/5 21:27
 * @Ink memory www.tothefor.com com
 */
public class Main {
    public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    public static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    public static Scanner sc = new Scanner(System.in);

    public static int maxd = 2000+7;
    public static int INF = 0x3f3f3f3f;
    public static int mod = 998244353;

    public static int[] head = new int[maxd];
    public static int[] edgePre = new int[maxd];
    public static int[] edgeTo = new int[maxd];
    public static int node = 0;
    public static int[] match = new int[maxd]; //Who's the boyfriend used to store my sister
    public static boolean[] vis = new boolean[maxd]; //For a boy, it is used to save whether the girl has been selected by the boy, so as to ensure that the girl only appears once in a boy's life

    public static void init(int n){
        for(int i=1;i<=n;++i){
            head[i]=-1;
        }
        node=0;
    }
    public static void add_edge(int a,int b){
        edgeTo[node]=b;
        edgePre[node]=head[a];
        head[a]=node++;
    }
    public static boolean find(int x){ //Can you help x find his girlfriend
        for(int i=head[x];i!=-1;i=edgePre[i]){
            int v = edgeTo[i];
            if(!vis[v]){ //If you haven't been with this girl before, give it a try. Otherwise, it's an ex girlfriend. Don't
                vis[v]=true; //Not before, try it now (but maybe someone else has a boyfriend now)
                if(match[v]==0||find(match[v])){ //If you don't have a boyfriend, you can; If you have a boyfriend, see if you can find another one for her boyfriend
                    match[v] = x; //No boyfriend or found one for her boyfriend
                    return true;
                }
            }
        }
        return false; //I tried all kinds of ways and couldn't find my girlfriend
    }

    public static void main(String[] args) throws Exception {

        int n1 = nextInt(); //Left set (number of boys)
        int n2 = nextInt(); //Right set (number of girls)
        int m = nextInt(); //Number of sides (number of relationships)
        init(n1);
        while(m-->0){
            int a = nextInt();
            int b = nextInt();
            add_edge(a,b); //Since the left points to the right (men choose women), there is no need to point to the left (women choose men)
        }

        int ans = 0;
        for(int i=1;i<=n1;++i){ //Find girlfriends for all boys
            Arrays.fill(vis,false); //At present, boys and all other girls have never started
            if(find(i)) ans++;
        }
        System.out.println(ans);
        closeAll();
    }

    public static void cinInit(){
        cin.wordChars('a', 'z');
        cin.wordChars('A', 'Z');
        cin.wordChars(128 + 32, 255);
        cin.whitespaceChars(0, ' ');
        cin.commentChar('/');
        cin.quoteChar('"');
        cin.quoteChar('\'');
        cin.parseNumbers(); //Can be used alone to restore numbers
    }

    public static int nextInt() throws Exception{
        cin.nextToken();
        return (int) cin.nval;
    }
    public static long nextLong() throws Exception{
        cin.nextToken();
        return (long) cin.nval;
    }
    public static double nextDouble() throws Exception{
        cin.nextToken();
        return cin.nval;
    }
    public static String nextString() throws Exception{
        cin.nextToken();
        return cin.sval;
    }
    public static void closeAll() throws Exception {
        cout.close();
        in.close();
        out.close();
    }

}

Maximum matching - an idea of one's own

Tip: the correctness is not verified. It's just an idea. Use greedy thoughts. The one who has the least relationship with the opposite sex every time.

  • int man[999]; man[i] indicates the number of relationships between boys and girls numbered I.

  • int woman[999]; woman[i] indicates the number of relationships between girls and boys numbered I.

  • bool vis[999]; vis[i] indicates that the girl numbered I has matched.

First sort by the number of relationships between boys and girls (in ascending order), and then start from the people in a certain gender set (both men and women are OK). Here, start from the boys set. The idea is to start from male number one and find a special girl among the girls known by male number one. This special girl is satisfied: she knows the least boys and does not match with other boys successfully.

Then male number two and male number three.

Topics: Java