二分图的概念
二分图是图中的一种特殊模型,如果图的顶点v可以分割成两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则该图G成为一个二分图。
二分图如下
本文通过一个问题来引出二分图的最大匹配算法 。以素数伴侣的题目为例:若两个正整数的和为素数,则这两个数称为素数伴侣,设计程序从已有的N个正整数中挑出若干个对数组成素数伴侣,问最多能挑多少对?
分析:偶数除了2以外都不为素数,而两个偶数之和与两个奇数之和都为偶数。则仅有奇数和偶数才能组成素数伴侣。则为了解决该问题我们将奇数分为一类,偶数分为一类,若在两类中元素之和为素数则在两类之间连接一根线,解决该问题就是求出最多连线的组合即可。两类元素则可认为是一个二分图,使用匈牙利算法可以很好的解决这个问题。
匈牙利算法介绍如下,但是在介绍匈牙利算法前必须了解增广路,匹配边,未匹配边,匹配点,未匹配点的概念。匹配边就是在二分图中在两个点集之间连接的边,匹配边两端都是匹配点。未匹配边是在二分图中在两个点集之间连接的边,未匹配边一端是未匹配点一端是匹配点。增广路就是在二分图中从未匹配点开始,按照未匹配边,匹配边交替的模式找到一个未匹配点结束。在这里请仔细思考一下:找到未匹配点代表什么?
如果走增广路找到一个未匹配点之后,我们将增广路之中的匹配边断开。则第一个未匹配点与第一个匹配点构成一条匹配边。第二个匹配点与最后一个未匹配点构成第二条匹配边。就意味着我们找到了一条匹配边。
如下图:
图中1-4之间的边是匹配边,顶点是匹配点。2-5之间的变是匹配边,顶点是匹配点。则我们现在从未匹配点出发寻找一条增广路,从顶点3出发,按照未匹配边,匹配边的模式寻找增广路。3-4未匹配边,4-1匹配边,1-5未匹配边,5-2匹配边,2-6未匹配边,6为未匹配点,找到一条增广路。将匹配边断开,会发现图中多了一条匹配边,如下:
样例输入:
输出:
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)。