1. 程式人生 > 實用技巧 >[筆記]二分圖最大匹配--匈牙利演算法

[筆記]二分圖最大匹配--匈牙利演算法

[筆記]二分圖最大匹配--匈牙利演算法

原題鏈

演算法:

0.首先有一些概念:

​ ①.二分圖:設G=(V,E)是一個無向圖,如果頂點V可分割為兩個互不相交的子集(A,B),並且圖中的每條邊(i,j)所關聯的兩個頂點i和j分別屬於這兩個不同的頂點集(i in A,j in B),則稱圖G為一個二分圖。(我並沒有看懂上面這一坨,我的理解是可以分成兩個相互獨立部分的圖就是二分圖)

​ ②匹配:給定一個二分圖G,在G的一個子圖M中,M的邊集{E}中的任意兩條邊都不依附於同一個頂點,則稱M是一個匹配。

​ ③:最大匹配:最大匹配即是選擇其中邊數最大的子集的圖。

​ ④:增廣路:若P是圖G中一條連通兩個未匹配頂點的路徑,並且屬於M的邊和不屬於M的邊(即已匹配和待匹配的邊)在P上交替出現,則稱P為相對於M的一條增廣路徑(舉例來說,有A、B集合,增廣路由A中一個點通向B中一個點,再由B中這個點通向A中一個點……交替進行)

​ 我們通過以上概念可以知道,二分圖的一組匹配是最大匹配當且僅當圖中不存在增廣路.

1.演算法步驟

​ ①首先所有邊都是非匹配邊

​ ②尋找增廣路,並將增廣路兩端的點設為已匹配的點

​ ③重複步驟②,知道圖中不再有增廣路

2.詳細過程

​ 具體說一說第②步:

​ 要想新建一條增廣路需要滿足一下兩個條件中的任意一個:

​ 1.右半部分的點y自身為非匹配點

​ 2.右部分的點y為匹配點,但是與它相連的左部分的點x可以在右部分找到另一個點進行匹配

AC程式碼

#include <bits/stdc++.h>
using namespace std;
bool mapp[5200][5200],vis[5200];
int match[5200];//記錄左部分匹配的物件
int n,m,s;
bool dfs(int k){
	for(int i = n + 1;i <= n + m;i++){
		if(!vis[i] && mapp[k][i]){//兩點之間有連邊,同時沒有被訪問過
			vis[i] = true;
			if(!match[i] || dfs(match[i])){//滿足上面說的兩個條件
				match[i] = k;
				match[k] = i;
				return true;
			}
		}
	}
	return false;
}
int main(){
	cin>>n>>m>>s;
	for(int i = 1;i <= s;i++){
		int x,y;
		cin>>x>>y;
		if(x > n || y > m)continue;
		y += n;//處理一下,因為題中給的左右部分的編號相同,不方便標記是否訪問過
		mapp[x][y] = mapp[y][x] = true;
	}
	int ans = 0;
	for(int i = 1;i <= n;i++){
		memset(vis,false,sizeof(vis));
		for(int j = 1;j <= n;j++)//預設從左部分向右部分匹配,因此將左部分預設成已訪問過
			vis[j] = true;
		if(dfs(i))ans++;
	}
	cout<<ans<<endl;
	return 0;
}

結束