程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> HDU 2063過山車 解題報告(我的第一道二分匹配)

HDU 2063過山車 解題報告(我的第一道二分匹配)

編輯:C++入門知識

下面是復制別人的解析後根據我不懂的地方自己補充修改的:
二部圖(也叫二分圖)概念:
 1.何為二部圖
  如果V(G)可以分到兩個集合X,Y中,且X和Y內部沒有G的邊.那麼圖G就是一個二部圖(等價於圖G是可二頂點著色的)下圖便是一個二部圖.
  
2.二部圖的性質
  一個圖是二部圖當且僅當圖G中沒有奇環.比如說一個三角形就不可能分成兩個部分,並且每個部分內部沒有邊,但一個正方形就可以.
3.如何得到二部圖的每個部分
  任意選一個頂點,所有到該點距離為偶數的點構成的集合便是G中的一部分,距離為奇數的點為另一部分
4.何為匹配
  圖G的一個匹配是一組沒有公共端點的邊構成的集合,如(圖一)兩條黑色的邊(1,4、2,5)構成一個大小為2的匹配,三條紅色的邊(1,4、2,5、3,6)構成一個大小為3的匹配.圖中的最大匹配數就是3。
該題就是求解一個二分圖的最大匹配,判定一個匹配是不是最大匹配就是通過尋找增廣路徑。如何尋找呢?
假設該題中女生為X部,男生為Y部,定義幾個變量,G[u][i]代表u,i兩點之間的連接情況,marry[i] =u表示Y部中的第i個男生與女生u配對,visit[i] 表示Y部中第i個男生有沒有女生找過,兩個數組初始化為0。
在每次尋找增廣路徑時,我們都將visit[i]重置,因為每次尋找增廣路徑就是讓他們每個男生都有重新選擇的機會,然後判定這種新的匹配方式能否產生更多的匹配。
step1:從一個女生開始,掃描所有在另外一個部分(Y部)與之相連的點,沒有邊或者已經給過機會的男生(他們或許已經找到新另一半,或者他不願與前女伴分手)的不予考慮。
    for (int i = 1; i <= N; ++i) {
      if (!G[u][i] || visit[i])
        continue;
      ......
    }
step2:兩兩之間有邊,並且第i個男生在這一輪新的配對中暫時沒有被女生找到的話(就是step1的if判定失敗),那麼這個男生就算被女生找過了。
    visit[i] = 1;
step3:現在我們這樣來辦,如果第i個男生之前沒有女生配對的話,那麼馬上將他們聯系起來,因為這必將是新的一對,並且返回1,表示尋找到了增廣路徑。還有一種情況,那就
    該男生之前有女生配對,那麼我們就策劃讓其以前配對的女生另外找一個男生,這不難實現,再次調用這個函數即可。
     if (!marry[i] || path(marry[i])) {
      marry[i] = u;
      return 1;
    }
step4:如果所有的男生由於各種原因都不願與該女生配對的話,返回0,表示尋找增廣路徑失敗。
      我們調從外部調用這個函數N次(N代表女生的個數),表示每次抱著給這個女生找有好男友的決心,雖然過程中可能會拆散其他對,但我能保證只有當新配對的人數多余上次匹配結果我才這樣去做。
  注意:1.假設是第k次從外部調用該函數的話,執行該函數的過程中一定不會牽涉到第k+1到N號女生的匹配情況,因為我們每次頂多是拆散以前的配對過的女生去完成新匹配。
       2.為什麼能保證配對的對數增加呢?如果函數執行成功,我們為第k號女生完成了匹配,並且為所有為之被拆散的女生找到了新的對象,所以匹配數會增加1。
  一個匹配M是圖G的最大匹配當且僅當圖G中不存在M-增廣路徑. M-增廣路徑是一條邊交替出現在匹配M和不出現在匹配M中的路徑,且兩個端點沒有被M中的邊覆蓋。若是一個圖有M-增廣路徑,就得到了一個更大的匹配。所謂交替出現在M和不出現在M中的路徑就是撮合一對,拆散一對的過程。


 
代碼如下:

 

[cpp] 
#include<iostream> 
#include<stdio.h> 
#include<math.h> 
#include<string.h> 
#include<stdlib.h> 
#include<algorithm> 
using namespace std; 
int map[505][505],vb[505],marryb_g[505]; 
int k,n,m; 
 
int getpath(int u) 

    int i; 
    for(i=1;i<=m;i++) 
    { 
        if(!map[u][i] || vb[i]) 
            continue; 
        vb[i]=1; 
        if(!marryb_g[i] || getpath(marryb_g[i])) 
        { 
            marryb_g[i]=u; 
            return 1; 
        } 
    } 
    return 0; 

 
int main() 

 
    int i,a,b; 
    while(scanf("%d",&k)!=EOF && k) 
    { 
        memset(map,0,sizeof(map)); 
        memset(marryb_g,0,sizeof(marryb_g)); 
        scanf("%d%d",&n,&m); 
        for(i=0;i<k;i++) 
        { 
            scanf("%d%d",&a,&b); 
            map[a][b]=1; 
        } 
        int ans=0; 
        for(i=1;i<=n;i++) 
        { 
            memset(vb,0,sizeof(vb)); 
            if(getpath(i)) 
                ans++; 
        } 
        printf("%d\n",ans); 
    } 
    return 0; 

作者:cxb569262726

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved