1. 程式人生 > >hdu 2063 過山車 (二分圖匹配)

hdu 2063 過山車 (二分圖匹配)

判斷是否為二分圖:

定理:一個無向圖G=<V,E>是二分圖當且僅當G中無奇數長度的迴路。

匈牙利演算法:

1.對於左邊X的每個點,看看右邊Y有沒有增廣路,如果有,那麼進行增廣,沒有就不新增新的匹配。 2.當對最後一個點做完增廣路以後,整個圖就形成了一個最大匹配。

尋找交錯路徑(增廣路)

                         

                圖1               圖2                  

(1)有奇數條邊。

 (2)起點在二分圖的左半邊,終點在右半邊。

 (3)路徑上的點一定是一個在左半邊,一個在右半邊,交替出現。

(4)整條路徑上沒有重複的點。

(5)起點和終點都是目前還沒有配對的點,而其它所有點都是已經配好對的。(如圖1、圖2所示,[1,5]和[2,6]在圖1中是兩對已經配好對的點;而起點3和終點4目前還沒有與其它點配對。) 

(6)路徑上的所有第奇數條邊都不在原匹配中,所有第偶數條邊都出現在原匹配中。(如圖1、圖2所示,原有的匹配是[1,5]和[2,6],這兩條配匹的邊在圖2給出的增廣路徑中分邊是第2和第4條邊。而增廣路徑的第1、3、5條邊都沒有出現在圖1給出的匹配中。) 

(7)最後,也是最重要的一條,把增廣路徑上的所有第奇數條邊加入到原匹配中去,並把增廣路徑中的所有第偶數條邊從原匹配中刪除(這個操作稱為增廣路徑的截斷),則新的匹配數就比原匹配數增加了1個。(如圖2所示,新的匹配就是所有藍色的邊,而所有紅色的邊則從原匹配中刪除。則新的匹配數為3。)

------------------------------------------------------------------------------------------------------------------------------------

該題就是求解一個二分圖的最大匹配,判定一個匹配是不是最大匹配就是通過尋找增廣路徑。 假設該題中女生為X部,男生為Y部,定義幾個變數,map[u][i]代表u,i兩點之間的連線情況,p[i] =u表示Y部中的第i個男生與女生u配對,

vis[i] 表示Y部中第i個男生有沒有女生找過,兩個陣列初始化為0。

在每次尋找增廣路徑時,我們都將vis[i]重置,因為每次尋找增廣路徑就是讓他們每個男生都有重新選擇的機會,然後判定這種新的匹配方式能否產生更多的匹配。

step1:從一個女生開始,掃描所有在另外一個部分(Y部)與之相連的點,沒有邊或者已經給過機會的男生(他們或許已經找到新另一半,或者他不願與前女伴分手)的不予考慮。     for (int i = 1; i <= N; ++i) {       if (!map[u][i] || visit[i])         continue;       ......     }

step2:兩兩之間有邊,並且第i個男生在這一輪新的配對中暫時沒有被女生找到的話(就是step1的if判定失敗),那麼這個男生就算被女生找過了。     vis[i] = 1;

step3:現在我們這樣來辦,如果第i個男生之前沒有女生配對的話,那麼馬上將他們聯絡起來,因為這必將是新的一對,並且返回1,表示尋找到了增廣路徑。

還有一種情況,那就是該男生之前有女生配對,那麼我們就策劃讓其以前配對的女生另外找一個男生,這不難實現,再次呼叫這個函式即可。      if (!p[i] || findpath(p[i]))

{       p[i] = u;       return 1;     }

step4:如果所有的男生由於各種原因都不願與該女生配對的話,返回0,表示尋找增廣路徑失敗。

   我們從外部呼叫這個函式M次(M代表女生的個數),表示每次抱著給這個女生找有好男友的決心,雖然過程中可能會拆散其他對,但我能保證只有當新配對的人數多餘上次匹配結果我才這樣去做。   注意:1.假設是第k次從外部呼叫該函式的話,執行該函式的過程中一定不會牽涉到第k+1到M號女生的匹配情況,因為我們每次頂多是拆散以前的配對過的女生去完成新匹配。       2.為什麼能保證配對的對數增加呢?如果函式執行成功,我們為第k號女生完成了匹配,並且為所有為之被拆散的女生找到了新的物件,所以匹配數會增加1。   一個匹配M是圖G的最大匹配當且僅當圖G中不存在M-增廣路徑. M-增廣路徑是一條邊交替出現在匹配M和不出現在匹配M中的路徑,且兩個端點沒有被M中的邊覆蓋。若是一個圖有M-增廣路徑,就得到了一個更大的匹配。所謂交替出現在M和不出現在M中的路徑就是撮合一對,拆散一對的過程。

#include <iostream> #include <string.h> using namespace std; int map[501][501],vis[501],p[501]; int K,M,N,a,b; bool findpath(int x) {     for(int i=1;i<=N;i++)     {         if(!vis[i]&&map[x][i])         {             vis[i]=1;             if(!p[i]||findpath(p[i]))             {                 p[i]=x;                 return true;             }         }     }     return false; } int main() {     while(cin>>K&&K!=0)     {         cin>>M>>N;         memset(map,0,sizeof(map));         memset(p,0,sizeof(p));         for(int i=0;i<K;i++)         {             cin>>a>>b;             map[a][b]=1;         }         int sum=0;         for(int i=1;i<=M;i++)         {             memset(vis,0,sizeof(vis));             if(findpath(i))                 sum++;         }         cout<<sum<<endl;     }     return 0; }