二分圖匹配問題


一、

二分圖基礎:

參考鏈接:https://blog.csdn.net/jeryjeryjery/article/details/79596922

       https://www.cnblogs.com/penseur/archive/2013/06/16/3138981.html

什么叫二分圖:給你一些點,其中這些點之間的某兩個點是相連的。如果你可以把全部點分成兩個集合 且 每個集合的任意兩個點之間沒有連線

       也就是說對於任意一條邊它的兩個點不能來自於一個集合,就比如:

 

 

      1,2,3一個集合,其他點一個集合就是二分圖!

染色法判斷是否為一個二分圖:https://blog.csdn.net/li13168690086/article/details/81506044

算法過程:用兩種顏色,對所有頂點逐個染色,且相鄰頂點染不同的顏色,如果發現相鄰頂點染了同一種顏色,就認為此圖不為二分圖。 當所有頂點都                       被染色,且沒有發現同色的相鄰頂點,就退出。

NYOJ 1015 二部圖為例題:

代碼:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <string.h>
 4 #include <stdio.h>
 5 #include <math.h>
 6 using namespace std;
 7 const int N = 505;
 8 int m,n;
 9 int color[N];
10 int edge[N][N];
11 bool dfs(int v, int c){
12     color[v] = c;    //將當前頂點塗色
13     for(int i = 0; i < n; i++){    //遍歷所有相鄰頂點,即連着的點
14         if(edge[v][i] == 1){    //如果頂點存在
15             if(color[i] == c)    //如果顏色重復,就返回false
16                 return false;
17             if(color[i] == 0 && !dfs(i,-c))    //如果還未塗色,就染上相反的顏色-c,並dfs這個頂點,進入下一層
18                 return false;   //返回false
19         }
20     }
21     return true;   //如果所有頂點塗完色,並且沒有出現同色的相鄰頂點,就返回true
22 }
23 void solve(){
24     for(int i = 0; i < n; i++){
25         if(color[i] == 0){
26             if(!dfs(i, 1)){
27                 printf("NOT BICOLORABLE.\n");
28                 return;
29             }
30         }
31     }
32     printf("BICOLORABLE.\n");
33 }
34 int main(){
35     int u,v;
36     while(cin >> n >> m){
37         memset(color, 0, sizeof(color));
38         memset(edge, 0, sizeof(edge));
39         for(int i = 0; i < m; i++){
40             cin >> u >> v;    //因為是無向圖,所以要往兩個方向添加邊
41             edge[u][v] = 1;    //正着添加
42             edge[v][u] = 1;    //反着添加
43         }
44         solve();
45     }
46     return 0;
47 }
View Code

 

 

什么叫匹配:圖中匹配的定義是指,這個圖的一個邊的集合,集合中任意兩條邊都沒有公共的頂點,則稱這個邊集為一個匹配。我們稱匹配中的邊匹                       配邊,邊中的點為匹配點;未在匹配中的邊為非匹配邊,邊中的點為未匹配點。就比如:

  

 

 

       這就是一個匹配,每一個點只能使用一次,但是它並不是最大匹配,因為我們可以讓2和8相連,這樣就會多一條匹配邊3->5

       要注意所有匹配邊都是原來圖中存在的邊,只是我們選擇了這些邊中的某幾條

最大匹配: 一個圖中所有匹配中,所含匹配邊數最大的匹配稱為最大匹配。

完美匹配: 如果一個圖的某個匹配中,圖的所有頂點都是匹配點,那么這個匹配就是完美匹配。很顯然,完美匹配一定是最大匹配,但是並不是所有的                        圖都存在完美匹配。

交替路: 從一個未匹配點出發,依次經過非匹配邊、匹配邊、非匹配邊、匹配邊…,形成這樣的交替進行的路徑成為交替路。

什么是增廣路: 從一個未匹配點出發,走交替路,如果途徑一個未匹配點(出發點不算),則這樣一條交替路稱為增廣路。增廣路有一個重要的特性,就                                 是非匹配邊要比匹配邊多一條(從未匹配點出發,以未匹配點結束,走交替路,顯然非匹配邊多一條),此時,我們可以讓增廣路中的匹                               配邊和非匹配邊調換位置,匹配邊變為非匹配邊,非匹配邊變為匹配邊,這樣匹配邊的數量就會加1,並且由於中間的匹配節點不存在其                               他相連的匹配邊,所以這樣做不會破壞匹配的性質,保證了交換的正確性。

匈牙利算法: 算法就是根據增廣路的思想,以一個未匹配的節點出發,遍歷圖,不斷的尋找增廣路來擴充匹配的邊數,直到不能擴充為止。根據遍歷圖的                         方式不同,匈牙利算法可以分為dfs(深度遍歷)和bfs(廣度遍歷)的實現。

匈利牙利算法復雜度:這個問題既可以利用最大流算法解決也可以用匈牙利算法解決。如果用最大流算法中的Edmonds-karp算法解決,因為時間復雜度                                         為O(n*m*m),n為點數,m為邊數,會超時,利用匈牙利算法,時間復雜度為O(n*m),時間復雜度小,不會超時。

 

 

 

 

 

 

以HDU - 1083 Courses為例子:

題意:給你p個課程和n個學生,你可不可以從這k個學生中找出來p個學生,使得每一個學生負責一個課程。后面會給出課程和上這個課的學生

   只要這個學生上這個課就可以負責這個課程

代碼:

 

 1 #include<stdio.h>
 2 #include<algorithm>
 3 #include<string.h>
 4 #include<iostream>
 5 #include<queue>
 6 using namespace std;
 7 const int maxn=305;
 8 int match[maxn],visit[maxn],n,m,grap[maxn][maxn];
 9 int dfs_solve(int x)
10 {
11     for(int i=1;i<=n;++i)
12     {
13         if(grap[x][i] && !visit[i])
14         {
15             visit[i]=1;
16             if(match[i]==0 || dfs_solve(match[i]))
17             {
18                 match[i]=x;
19                 return 1;
20             }
21         }
22     }
23     return 0;
24 }
25 int hungran()
26 {
27     memset(match,0,sizeof(match));
28     int sum=0;
29     for(int i=1;i<=m;++i)
30     {
31         memset(visit,0,sizeof(visit));
32         sum+=dfs_solve(i);
33     }
34     return sum;
35 }
36 int main()
37 {
38     int t;
39     scanf("%d",&t);
40     while(t--)
41     {
42         scanf("%d%d",&m,&n);
43         memset(grap,0,sizeof(grap));
44         for(int i=1;i<=m;++i)
45         {
46             int q;
47             scanf("%d",&q);
48             while(q--)
49             {
50                 int w;
51                 scanf("%d",&w);
52                 grap[i][w]=1;
53             }
54         }
55         int ans=hungran();
56         if(ans==m)
57         {
58             printf("YES\n");
59         }
60         else printf("NO\n");
61     }
62     return 0;
63 }
View Code

 

Hopcroft-Karp算法:這個算法的時間復雜度為O(n^(1/2)*m)。該算法是對匈牙利算法的優化。利用匈牙利算法一次只能找到一條增廣路徑,Hopcroft-K                                        arp就提出一次找到多條不相交的增廣路徑(不相交就是沒有公共點和公共邊的增廣路徑),然后根據這些增廣路徑添加多個匹配。                                      說白了,就是批量處理!

具體過程看下圖:

 

 

還是以上一道題為例子:

  1 #include<iostream>                                                                                                         
  2  #include<queue>                                                                                                            
  3  using namespace std;                                                                                                       
  4  const int MAXN=500;// 最大點數                                                                                             
  5  const int INF=1<<28;// 距離初始值                                                                                          
  6  int bmap[MAXN][MAXN];//二分圖                                                                                              
  7                                                                                                                             
  8  int cx[MAXN];//cx[i]表示左集合i頂點所匹配的右集合的頂點序號                                                                
  9  int cy[MAXN]; //cy[i]表示右集合i頂點所匹配的左集合的頂點序號                                                               
 10                                                                                                                             
 11  int nx,ny;                                                                                                                 
 12  int dx[MAXN];                                                                                                              
 13  int dy[MAXN];                                                                                                              
 14  int dis;                                                                                                                   
 15  bool bmask[MAXN];                                                                                                          
 16  //尋找 增廣路徑集                                                                                                          
 17  bool searchpath()                                                                                                          
 18  {                                                                                                                          
 19     queue<int>Q;                                                                                                            
 20     dis=INF;                                                                                                                
 21     memset(dx,-1,sizeof(dx));                                                                                               
 22     memset(dy,-1,sizeof(dy));                                                                                               
 23     for(int i=1;i<=nx;i++)                                                                                                  
 24     {                                                                                                                       
 25        //cx[i]表示左集合i頂點所匹配的右集合的頂點序號                                                                       
 26        if(cx[i]==-1)                                                                                                        
 27        {                                                                                                                    
 28           //將未遍歷的節點 入隊 並初始化次節點距離為0                                                                       
 29           Q.push(i);                                                                                                        
 30           dx[i]=0;                                                                                                          
 31        }                                                                                                                    
 32     }                                                                                                                       
 33     //廣度搜索增廣路徑                                                                                                      
 34     while(!Q.empty())                                                                                                       
 35     {                                                                                                                       
 36        int u=Q.front();                                                                                                     
 37        Q.pop();                                                                                                             
 38        if(dx[u]>dis) break;                                                                                                 
 39        //取右側節點                                                                                                         
 40        for(int v=1;v<=ny;v++)                                                                                               
 41        {                                                                                                                    
 42           //右側節點的增廣路徑的距離                                                                                        
 43           if(bmap[u][v]&&dy[v]==-1)                                                                                         
 44           {                                                                                                                 
 45              dy[v]=dx[u]+1; //v對應的距離 為u對應距離加1                                                                    
 46              if(cy[v]==-1) dis=dy[v];                                                                                       
 47              else                                                                                                           
 48              {                                                                                                              
 49                 dx[cy[v]]=dy[v]+1;                                                                                          
 50                 Q.push(cy[v]);                                                                                              
 51              }                                                                                                              
 52           }                                                                                                                 
 53        }                                                                                                                    
 54     }                                                                                                                       
 55     return dis!=INF;                                                                                                        
 56  }                                                                                                                          
 57                                                                                                                             
 58  //尋找路徑 深度搜索                                                                                                        
 59  int findpath(int u)                                                                                                        
 60  {                                                                                                                          
 61     for(int v=1;v<=ny;v++)                                                                                                  
 62     {                                                                                                                       
 63        //如果該點沒有被遍歷過 並且距離為上一節點+1                                                                          
 64        if(!bmask[v]&&bmap[u][v]&&dy[v]==dx[u]+1)                                                                            
 65        {                                                                                                                    
 66           //對該點染色                                                                                                      
 67           bmask[v]=1;                                                                                                       
 68           if(cy[v]!=-1&&dy[v]==dis)                                                                                         
 69           {                                                                                                                 
 70              continue;                                                                                                      
 71           }                                                                                                                 
 72           if(cy[v]==-1||findpath(cy[v]))                                                                                    
 73           {                                                                                                                 
 74              cy[v]=u;cx[u]=v;                                                                                               
 75              return 1;                                                                                                      
 76           }                                                                                                                 
 77        }                                                                                                                    
 78     }                                                                                                                       
 79     return 0;                                                                                                               
 80  }                                                                                                                          
 81                                                                                                                             
 82  //得到最大匹配的數目                                                                                                       
 83  int MaxMatch()                                                                                                             
 84  {                                                                                                                          
 85     int res=0;                                                                                                              
 86     memset(cx,-1,sizeof(cx));                                                                                               
 87     memset(cy,-1,sizeof(cy));                                                                                               
 88     while(searchpath())                                                                                                     
 89     {                                                                                                                       
 90        memset(bmask,0,sizeof(bmask));                                                                                       
 91        for(int i=1;i<=nx;i++)                                                                                               
 92        {                                                                                                                    
 93           if(cx[i]==-1)                                                                                                     
 94           {                                                                                                                 
 95              res+=findpath(i);                                                                                              
 96           }                                                                                                                 
 97        }                                                                                                                    
 98     }                                                                                                                       
 99     return res;                                                                                                             
100  }                                                                                                                          
101                                                                                                                             
102                                                                                                                             
103  int main()                                                                                                                 
104  {                                                                                                                          
105     int num;                                                                                                                
106     scanf("%d",&num);                                                                                                       
107     while(num--)                                                                                                            
108     {                                                                                                                       
109                                                                                                                             
110        memset(bmap,0,sizeof(bmap));                                                                                         
111        scanf("%d%d",&nx,&ny);                                                                                               
112        for(int i=1;i<=nx;i++)                                                                                               
113        {                                                                                                                    
114           int snum;                                                                                                         
115           scanf("%d",&snum);                                                                                                
116           int u;                                                                                                            
117           for(int j=1;j<=snum;j++)                                                                                          
118           {                                                                                                                 
119              scanf("%d",&u);                                                                                                
120              bmap[i][u]=1;                                                                                                  
121             // bmap[u][i]=1;                                                                                                
122           }                                                                                                                 
123        }                                                                                                                    
124       // cout<<MaxMatch()<<endl;                                                                                            
125        if(MaxMatch()==nx)                                                                                                   
126        {                                                                                                                    
127           printf("YES\n");                                                                                                  
128        }                                                                                                                    
129        else                                                                                                                 
130        {                                                                                                                    
131           printf("NO\n");                                                                                                   
132        }                                                                                                                    
133     }                                                                                                                       
134     //system("pause");                                                                                                      
135     return 0;                                                                                                               
136  }                                                                                                                          
137                                                                                                                             
138  /*                                                                                                                         
139                                                                                                                          
140 4                                                                                                                        
141 1 3                                                                                                                      
142 1 3 4                                                                                                                    
143 2                                                                                                                        
144                                                                                                                             
145                                                                                                                             
146  */
View Code

 

二、

二分圖的使用:

1、最小頂點覆蓋:

        在圖中所有的點中找出來最小的點集合,使得圖中的每一條邊的兩個頂點至少 有一個在這個點集中

        結論:最小頂點覆蓋 == 最大匹配      

      證明:假設當前存在一條兩個端點都不在最小頂點覆蓋點集中,那么這么這條邊一定可以增大最大匹配邊集,與最大匹配矛盾。

2、最小路徑覆蓋:在圖中找一個最小的邊集合,使得圖中的每一個點都可以在這個邊集合的斷點處找到。

      結論:最小路徑覆蓋 == 頂點數 - 最大匹配

      證明:因為一條邊最多可以包含兩個頂點,所以我們選邊的時候讓這樣的邊盡量 多,也就是說最大匹配的邊集合中邊的數目。剩下的點就                                     只能一個邊連上一 個點到集合里。

                                注意:最大匹配得到的是邊的個數,(2*最大匹配)才是點的個數。所以(頂點數 - 最大匹配)得到的是(最大匹配后剩余的點)和(最大匹                配的邊)

3、最大獨立集:在N個點中選出來一個最大點集合,使這個點集合中的任意兩點之間都沒有邊。

      結論:最大獨立集 == 頂點數 - 最大匹配

      證明:因為去掉最大匹配兩端的頂點去掉以后,剩下的點肯定是獨立集。我們再從每個匹配里面挑選出來一個點加入到獨立集中,也是不會破           壞原有獨立集的獨立性的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM