KM
const int INF = 0x3f3f3f3f;
const int N = 305;
int n, m, match[N], pre[N];
bool vis[N];
int favor[N][N];
int val1[N], val2[N], slack[N];
void bfs(int p) {
memset(pre, 0, sizeof pre);
memset(slack, INF, sizeof slack);
match[0] = p;
int x = 0, nex = 0;
do {
vis[x] = true;
int y = match[x], d = INF;
// 對於當前節點y,bfs有連邊的下一點
for (int i = 1; i <= m; i++) {
if (!vis[i]) {
if (slack[i] > val1[y] + val2[i] - favor[y][i]) {
slack[i] = val1[y] + val2[i] - favor[y][i];
pre[i] = x;
}
if (slack[i] < d) {
d = slack[i];
nex = i;
}
}
}
for (int i = 0; i <= m; i++) {
if (vis[i])
val1[match[i]] -= d, val2[i] += d;
else
slack[i] -= d;
}
x = nex;
} while (match[x]);
// pre陣列對bfs訪問路徑進行記錄,在最後一併改變match
while (x) {
match[x] = match[pre[x]];
x = pre[x];
}
}
int KM() {
memset(match, 0, sizeof match);
memset(val1, 0, sizeof val1);
memset(val2, 0, sizeof val2);
for (int i = 1; i <= n; i++) {
memset(vis, false, sizeof vis);
bfs(i);
}
int res = 0;
for (int i = 1; i <= m; i++)
res += favor[match[i]][i];
return res;
}
這個演算法是用於解決二分圖最大權匹配問題的Kuhn-Munkres演算法(簡稱KM演算法)。KM演算法透過逐步調整頂標(也稱為勢)來尋找最大權匹配。以下是對程式碼的詳細解釋:
變數解釋
n
和m
:分別表示二分圖的兩部分頂點數。match[N]
:記錄匹配資訊,match[i]
表示右邊第i
個頂點匹配的左邊頂點。pre[N]
:記錄增廣路徑的前驅節點。vis[N]
:記錄節點是否在當前增廣路徑中被訪問過。favor[N][N]
:表示二分圖中邊的權重。val1[N]
和val2[N]
:分別表示左邊和右邊頂點的頂標(勢)。slack[N]
:記錄每個右邊頂點與當前增廣路徑的最小差值。
函式解釋
bfs(int p)
這個函式用於尋找從左邊第 p
個頂點開始的增廣路徑。
-
初始化:
memset(pre, 0, sizeof pre);
:初始化前驅節點陣列。memset(slack, INF, sizeof slack);
:初始化slack
陣列為無窮大。match[0] = p;
:將p
作為起點。
-
迴圈尋找增廣路徑:
do
迴圈:vis[x] = true;
:標記當前節點x
為已訪問。int y = match[x], d = INF;
:獲取當前節點的匹配節點y
,並初始化最小差值d
為無窮大。- 對於當前節點
y
,遍歷所有右邊節點i
:- 如果
i
未被訪問:- 更新
slack[i]
為val1[y] + val2[i] - favor[y][i]
。 - 更新前驅節點
pre[i]
為x
。 - 如果
slack[i]
小於d
,則更新d
和nex
。
- 更新
- 如果
- 更新所有節點的頂標:
- 如果節點
i
被訪問,則val1[match[i]] -= d
和val2[i] += d
。 - 否則,
slack[i] -= d
。
- 如果節點
x = nex;
:更新當前節點為nex
。
while (match[x]);
:直到找到未匹配的節點。
-
更新匹配資訊:
while (x)
迴圈:match[x] = match[pre[x]];
:更新匹配資訊。x = pre[x];
:回溯前驅節點。
KM()
這個函式是KM演算法的主函式,用於求解二分圖的最大權匹配。
-
初始化:
memset(match, 0, sizeof match);
:初始化匹配陣列。memset(val1, 0, sizeof val1);
:初始化左邊頂標。memset(val2, 0, sizeof val2);
:初始化右邊頂標。
-
尋找增廣路徑:
- 對於每個左邊節點
i
,呼叫bfs(i)
尋找增廣路徑。
- 對於每個左邊節點
-
計算最大權匹配:
- 遍歷所有右邊節點
i
,累加匹配邊的權重favor[match[i]][i]
。
- 遍歷所有右邊節點
總結
KM演算法透過逐步調整頂標來尋找最大權匹配。bfs
函式用於尋找增廣路徑,並在找到增廣路徑後更新匹配資訊。KM
函式則是主函式,負責初始化和呼叫 bfs
函式,最終計算並返回最大權匹配的值。