zoukankan      html  css  js  c++  java
  • 二分图匹配问题

    一、

    二分图基础:

    参考链接: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.
    ");
    28                 return;
    29             }
    30         }
    31     }
    32     printf("BICOLORABLE.
    ");
    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
    ");
    59         }
    60         else printf("NO
    ");
    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
    ");                                                                                                  
    128        }                                                                                                                    
    129        else                                                                                                                 
    130        {                                                                                                                    
    131           printf("NO
    ");                                                                                                   
    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个点中选出来一个最大点集合,使这个点集合中的任意两点之间都没有边。

          结论:最大独立集 == 顶点数 - 最大匹配

          证明:因为去掉最大匹配两端的顶点去掉以后,剩下的点肯定是独立集。我们再从每个匹配里面挑选出来一个点加入到独立集中,也是不会破           坏原有独立集的独立性的。

  • 相关阅读:
    淀粉质模板 Tree
    洛谷 P2197 【模板】nim游戏 解题报告
    洛谷 P3168 [CQOI2015]任务查询系统 解题报告
    洛谷 P2485 [SDOI2011]计算器 解题报告
    洛谷 P4883 mzf的考验 解题报告
    洛谷 P4882 lty loves 96! 解题报告
    牛客 NOIp模拟1 T1 中位数 解题报告
    牛客 NOIp模拟1 T3 保护 解题报告
    洛谷 P3349 [ZJOI2016]小星星 解题报告
    洛谷 P4139 上帝与集合的正确用法 解题报告
  • 原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/11492152.html
Copyright © 2011-2022 走看看