二分圖最佳完美匹配
阿新 • • 發佈:2019-01-01
華電北風吹
天津大學認知計算與應用重點實驗室
日期:2016-07-15
一、二分圖的幾個相關定義:
最大匹配:匹配邊最多。
完美匹配:所有的頂點全部是匹配點。
最佳完美匹配:邊有權重,完美匹配中匹配邊權值和最大。
二、最佳完美匹配中需要了解的幾個定義
頂標:給每個頂點賦一個數值,成為該頂點的定標。
可行頂標:如果對於所有的邊,滿足此邊兩端的兩個頂點的定標和不小於此邊權重,則稱這一組頂標為可行頂標。
相等子圖:在當前頂標下,滿足邊兩端的兩個頂點的定標和正好等於此邊權重的所有邊,所構成的子圖。
三、KM演算法(Kuhn-Munkres演算法,匈牙利演算法)
對於二分圖的一部,初始定標為當前結點所連線邊權重最大值,另一部初始定標全為0,然後對於每個節點進行匹配。若相等子圖存在可擴充套件路徑則更新匹配關係,換下一個節點;若不存在擴充套件路徑,則更新定標,使匈牙利樹增加一條邊即可(由匈牙利樹的性質可知,這條邊必是從根節點同側匹配點到另一側非匹配點,根據這個更新對應點頂標即可)。具體更新規則見模板程式碼update部分。
四、參考程式碼
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 5
int W[maxn][maxn], n;
int Lx[maxn];
int Ly[maxn];
int Left[maxn];
bool S[maxn], T[maxn];
bool Match(int i)
{
S[i] = true;
for (int j = 1; j <= n; j++)
{
if (Lx[i] + Ly[j] == W[i][j] && !T[j])
{
T[j] = true ;
if (!Left[j] || Match(Left[j]))
{
Left[j] = i;
return true;
}
}
}
return false;
}
void Update()
{
int a = 1 << 30;
for (int i = 1; i <= n; i++)
{
if (S[i])
{
for (int j = 1; j <= n; j++)
{
if (!T[j])
{
a = min(a, Lx[i] + Ly[j] - W[i][j]);
}
}
}
}
for (int i = 1; i <= n; i++)
{
if (S[i])
Lx[i] -= a;
if (T[i])
Ly[i] += a;
}
}
void KM()
{
for (int i = 1; i <= n; i++)
{
Left[i] = 0;
Lx[i] = 0;
Ly[i] = 0;
for (int j = 1; j <= n; j++)
{
Lx[i] = max(Lx[i], W[i][j]);
}
}
for (int i = 1; i <= n; i++)
{
while (true)
{
memset(S, 0, sizeof(S));
memset(T, 0, sizeof(T));
if (Match(i))
break;
else
Update();
}
}
}
int main()
{
return 0;
}
五、擴充套件問題
覆蓋集:對於每條邊,至少要有一個點要被選中。
獨立集:對於每條邊,至少要有一個點不被選中。
二分圖的最大獨立集:選擇儘量多的點,使得任意兩個節點不相鄰(即任意一條邊的兩個端點不會同時被選中)。
二分圖的最小覆蓋::選擇儘量少的點,使得每條邊至少有一個端點被選中。
並且有最小覆蓋數加最大獨立集個數等於節點個數。
棋盤中的車,指派問題,最小集合覆蓋