二分图的概念

二分图是图中的一种特殊模型,如果图的顶点v可以分割成两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则该图G成为一个二分图。

二分图如下

数据结构与算法二分法 数据结构二分图_匈牙利算法


本文通过一个问题来引出二分图的最大匹配算法 。以素数伴侣的题目为例:若两个正整数的和为素数,则这两个数称为素数伴侣,设计程序从已有的N个正整数中挑出若干个对数组成素数伴侣,问最多能挑多少对?

分析:偶数除了2以外都不为素数,而两个偶数之和与两个奇数之和都为偶数。则仅有奇数和偶数才能组成素数伴侣。则为了解决该问题我们将奇数分为一类,偶数分为一类,若在两类中元素之和为素数则在两类之间连接一根线,解决该问题就是求出最多连线的组合即可。两类元素则可认为是一个二分图,使用匈牙利算法可以很好的解决这个问题。

匈牙利算法介绍如下,但是在介绍匈牙利算法前必须了解增广路,匹配边,未匹配边,匹配点,未匹配点的概念。匹配边就是在二分图中在两个点集之间连接的边,匹配边两端都是匹配点。未匹配边是在二分图中在两个点集之间连接的边,未匹配边一端是未匹配点一端是匹配点。增广路就是在二分图中从未匹配点开始,按照未匹配边,匹配边交替的模式找到一个未匹配点结束。在这里请仔细思考一下:找到未匹配点代表什么?

如果走增广路找到一个未匹配点之后,我们将增广路之中的匹配边断开。则第一个未匹配点与第一个匹配点构成一条匹配边。第二个匹配点与最后一个未匹配点构成第二条匹配边。就意味着我们找到了一条匹配边。

如下图:

数据结构与算法二分法 数据结构二分图_二分图_02


图中1-4之间的边是匹配边,顶点是匹配点。2-5之间的变是匹配边,顶点是匹配点。则我们现在从未匹配点出发寻找一条增广路,从顶点3出发,按照未匹配边,匹配边的模式寻找增广路。3-4未匹配边,4-1匹配边,1-5未匹配边,5-2匹配边,2-6未匹配边,6为未匹配点,找到一条增广路。将匹配边断开,会发现图中多了一条匹配边,如下:

数据结构与算法二分法 数据结构二分图_匈牙利算法_03

样例输入:

数据结构与算法二分法 数据结构二分图_二分图_04

输出:
3

import java.util.Arrays;
import java.util.Scanner;

//二分图最大匹配
//匈牙利算法  找增广路径的算法
public class erfentuzuidapipei {
    static int [][] e = new int[101][101];  //存储边
    static int [] match  = new int[101];   //记录是否配对
    static int [] book = new int[101];   //记录点是否已经被匹配过
    static int n,m;

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int t1,t2,sum=0;
        n = scan.nextInt();
        m = scan.nextInt();
        for (int i = 1; i <= m; i++) {
            t1 = scan.nextInt();
            t2 = scan.nextInt();
            e[t1][t2]=1;
            e[t2][t1]=1;
        }

        for (int i = 1; i <= n; i++){
            match[i] = 0;
        }

        System.out.println(prim());
    }

    public static int prim(){
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) book[j] = 0;
            if (dfs(i)) sum++;
        }
        return sum;
    }

    public static boolean dfs(int u){
        System.out.println("调用成功u="+u);
        for (int i = 1; i <= n; i++) {
            System.out.println(u+"  "+i+"准备匹配");
            if (book[i] == 0 && e[u][i] == 1){
                book[i] = 1;
                System.out.println("dafs");
                System.out.println("i="+i);
                if (match[i] == 0 || dfs(match[i])){
                    match[i] = u;
                    match[u] = i;
                    for (int j = 1; j <= n; j++) {
                        System.out.print(match[j]+" ");
                    }
                    System.out.println();
                    return true;
                }
            }
        }
        return false;
    }
}

总结:如果二分图有n个点,那么最多找到n/2条增广路径。

时间复杂度:
如果图中共有m条边,那么每找一条增广路径最多把所有边遍历一遍,所花时间是m。所以总的时间复杂度是O(NM)。