1. 程式人生 > >匈牙利演算法總結

匈牙利演算法總結

二分圖:

定義:如果一個圖的所有頂點可以被分為X和Y兩個集合,並且所有邊的兩個頂點恰好一個屬於集合X,另一個屬於集合Y,即每個集合內的頂點沒有邊相連,那麼此圖就是二分圖。

很多問題都可以轉化為二分圖匹配模型來計算。二分圖有如下幾種常見變形:

(1)最小頂點覆蓋

選取最少的點(X或Y中都行),讓每條邊都至少和其中一個點關聯。

Knoig定理:二分圖的最小頂點覆蓋數等於二分圖的最大匹配數。

(2)最小路徑覆蓋

對於一個DAG(有向無環圖),選取最少條路徑,使得每個頂點屬於且僅屬於一條路徑。

結論:DAG圖的最小路徑覆蓋數 = 節點數(n) - 最大匹配數(m).

(3)最大獨立集

選取最多的點,使任意所選兩點均不相連。

結論:二分圖的最大獨立集數 = 節點數(n) - 最大匹配數(m).

匈牙利演算法

匈牙利演算法就是一種用增廣路徑求二分圖最大匹配的演算法,核心是尋找增廣路徑。

演算法的時間複雜度為O(NM),其中N為二分圖左邊的頂點數,M為二分圖中邊的數目。

基本步驟:

1.首先從任意一個未被配對的點 u開始,從點u的邊中任意選一條邊(假設這條邊是u->v)開始配對。如果此時點V還沒有被配對,則配對成功,此時便找到了一條增廣路(只不過這條增廣路比較簡單)。如果此時點v已經被配對了,那就要嘗試進行“連鎖反應”。如果嘗試成功了,則找到一條增廣路,此時需要更新原來的配對關係。這裡要用一個數組match來記錄配對關係,比如點v與點u配對了,就記作match[v]=u。配對成功後,記得要將配對數加1。配對的過程我們可以通過深度優先搜尋來實現,當然廣度優先搜尋也可以。

2.如果剛才所選的邊配對失敗,要從點u的邊中再重新選一條邊, 進行嘗試。直到點u配對成功,或者嘗試過點u所有的邊為止。

3.接下來繼續對剩下沒有被配對的點一一進行配對, 直到所有的點都嘗試完畢,找不到新的增廣路為止。

核心程式碼:

for (int i = 1; i <= n; i++)
{
	for (int j = 1; j <= n; j++)
		vis[j] = 0;//清空上次搜尋時的標記
	if (dfs(i))
		sum++;//尋找增廣路,如果找到,配對數加1
}
int dfs(int u)
{
	for (int i = 1; i <= n; i++)
	{
		if (!vis[i] && map[u][i])
		{
			vis[i] = 1;//標記點i已經訪問過
			//如果點i未被配對或者找到了新的配對
			if (!match[i] || dfs(match[i]))
			{
				//更新配對關係
				match[i] = u;
				return 1;
			}
		}
	}
	return 0;
}