二分圖最大匹配與最小頂點覆蓋(教程系列)uva11419——我目前關於最大匹配最清晰的解釋。
阿新 • • 發佈:2019-01-03
定義什麼的百度拉拉,我只說證明.
1.假設我們現在已經用匈牙利演算法求出了最大匹配,很明顯現在已經木有增廣路了(即未匹配->匹配->未匹配這些形式的路徑,圖裡是木有的,不過一定要從下面說的那種特殊點開始)
2.現在我們從右邊開始標記一些點沿著(未匹配->匹配->未匹配......->匹配)這種形式的路徑進行,(特別注意的是這個起點一定沒有和匹配邊相鄰接,為什麼?看下面。)
標記路徑上經過的所有點。注意起點是右邊的點,結束時一定在匹配邊結束。
標記完後已經木有從右邊的這種點開始的未匹配邊了!!!(然後我將證明所有左邊已經標記的點和右邊未標記的點的數目和,就是最小覆蓋也等於最大匹配數)
3.對右邊的點分類,標記了的點,和沒標記的點,可以發現沒標記的點都有和匹配邊相連,顯然。。。而且沒標記的點對應的匹配邊的左邊那個點也是沒標記的。顯然。。
對於匹配邊也闊以分為兩類和右邊的沒標記的點相鄰接,和右邊的有標記的部分點相鄰接=左邊標記了的點數目,匹配邊數這樣分類時是木有重疊的,所以上述點數目和是最大匹配。
4.然後邊又闊以分為兩類。兩個端點都是標記點,兩個端點都是未標記點,嗯很顯然等於上面描述的點數目,綜上最大匹配數=最小覆蓋。
5.最小覆蓋<最大匹配是不可能的。。。
6.上述證明一定是正確的,我已經過了uva上的題了,用上面的證明。。。。。。。。。。(關鍵!!)不過要注意的對於那些度為0的點要排除掉!!!!!!!!!!!!!!!!!!!!!
此題就是把行當成左邊點,列是右邊點,然後n行m列有點的話讓左邊n號點連右邊m點然後就是求一個最小覆蓋。。
#include<iostream> #include<cstdio> #include<algorithm> #include<vector> using namespace std; vector<int>x[1050], y[1050]; int visitx[1050], visity[1050], matchy[1050],matchx[1050]; int getx[1050], gety[1050]; int n, m,point; bool dfs(int num) { visitx[num] = 1; for (int i = 0; i < x[num].size(); i++) { int to = x[num][i]; if (visity[to])continue;//說白了這一步你只能走未匹配邊如果visity[to]=1代表走的是匹配邊或者i正搜尋中你繼續走下去闊能有迴路 visity[to] = 1; //還有就算你visity[to]=1你走後然後後增廣你會發現那個y是和兩個x相連的。。。 if (matchy[to] == -1 || dfs(matchy[to]))//在dfs外面我們是一個一個遍歷x的但是當開始dfs後要到x必須走匹配邊而且是從y走所以這不用visitx[i]!=1這個條件 { matchy[to] = num; matchx[num] = to; return true; } } return false; } int getans(int nn,int mm) { int ans = 0; for (int i = 1; i <= nn; i++)matchx[i] = -1; for (int i = 1; i <= mm; i++)matchy[i] = -1; for (int i = 1; i <= nn; i++) { for (int j = 1; j <= nn; j++) visitx[j] = 0; for (int j = 1; j <= mm; j++) visity[j] = 0; if (dfs(i))ans++; } return ans; } void mdfs(int num) { gety[num] = 1; visity[num] = 1; for (int i = 0; i < y[num].size(); i++) { int to = y[num][i]; if (visitx[to])continue;//加速需要 getx[to] = 1; visitx[to] = 1; mdfs(matchx[to]); } } void mark(int nn, int mm)//每個點最多遍歷一次所以是o(n)的 { for (int i = 1; i <= nn; i++) getx[i] = 0, visitx[i] = 0; for (int i = 1; i <= mm; i++) gety[i] = 0, visity[i] = 0; for (int i = 1; i <= mm; i++) { if (visity[i]||y[i].size()==0)continue; if (matchy[i]==-1) mdfs(i); } } int main() { int k = 1; while (scanf("%d%d%d", &n, &m, &point)&&n&&m&&point) { for (int i = 1; i <= n; i++)x[i].clear(); for (int i = 1; i <= m; i++)y[i].clear(); for (int i = 0; i < point; i++) { int a, b; scanf("%d%d", &a, &b); x[a].push_back(b), y[b].push_back(a); } int ans = getans(n,m); mark(n,m); printf("%d", ans); for (int i = 1; i <= n; i++) { if (getx[i] == 1) printf(" r%d", i); } for (int i = 1; i <= m; i++) { if (gety[i] == 0&&y[i].size()!=0) printf(" c%d", i); } printf("\n"); } return 0; }