首页 > 代码库 > 二分匹配

二分匹配

PS:二分图匹配这一章的内容,我认为最重要的是要弄清楚概念。

一些定义:

二分图:二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如

果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(ij)所关联

的两个顶点ij分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个

二分图。记为G=(A,B;E)

二分图匹配:给定一个二分图GMG边集的一个子集,如果M满足当中的任意两条边都不依附于同一个顶点,则称M是一个匹配。

需要强调的是:集合A或者B中,本集合中的元素之间是没有关系的

 最大匹配可用最大流来解,最佳匹配可用最大流最小费用流来解。

二分匹配的意义何在,最大匹配、最佳匹配算法的时间复杂度低。

 

一、最大匹配

最大匹配是找到匹配数最多的匹配。

DFS版匈牙利算法: 代码最简洁。时间复杂度为O(n*m), 空间复杂度为O(n*n)

不卡时间大家都喜欢用这个。

BFS版匈牙利算法: 代码稍多。时间复杂度为O(n*m), 空间复杂度为O(n*n)

Hopcroft-Karp算法:对匈牙利算法进行了优化,时间复杂度O(m√n)。代码很长,空间复杂度为O(m*m)

需要说明的是:准备模版的时候还是多准备一些比较好,有些题目是卡时间,有些却是卡空间。所以有些题目用好的算法就是AC不了。

DFS匈牙利算法模版:

 1 //适合边比较多的图 2 const int N=300; 3 bool bmap[N][N],bmask[N]; 4 int cx[N],cy[N]; 5 int nx,ny,ans; 6 int findpath(int u) 7 { 8     int i,j; 9     for(i=1;i<=ny;i++)10     {11         if(bmap[u][i]&&!bmask[i])12         {13             bmask[i]=1;14             if(cy[i]==-1||findpath(cy[i]))15             {16                 cy[i]=u; cx[u]=i;17                 return 1;18             }19         }20     }21     return 0;22 }23 void maxmatch()24 {25     int i ,j;26     ans=0;27     for(i=1;i<=nx;i++) cx[i]=-1;28     for(i=1;i<=ny;i++) cy[i]=-1;29     for(i=1;i<=nx;i++)30     {31         if(cx[i]==-1)32         {33             for(j=1;j<=ny;j++)34                 bmask[j]=0;35             ans+=findpath(i);36         }37     }38 }39 void init()40 {41     memset(bmap,0,sizeof(bmap));42 }43 44 (2)匈牙利算法BFS    时间复杂度O(n*m)45 46 const int N=1010;47 int bmap[N][N],cx[N],cy[N],bmask[N],que[N],pre[N];48 int nx,ny,ans;49 void maxmatch()50 {51     ans=0;52     int qs,qe;53     memset(cx,-1,sizeof(cx));54     memset(cy,-1,sizeof(cy));55     memset(bmask,-1,sizeof(bmask));56     for(int i=1;i<=nx;i++)57     {58         if(cx[i] == -1)59         {60             qs=qe=0;61             que[qe++]=i;62             pre[i]=-1;63             bool flag=0;64             while(qs<qe&&!flag)65             {66                 int u=que[qs];67                 for(int v=1;v<=ny&&!flag;v++)68                 {69                     if(bmap[u][v]&&bmask[v]!=i)70                     {71                         bmask[v]=i;que[qe++]=cy[v];72                         if(cy[v]>=0) pre[cy[v]]=u;73                         else74                         {75                             flag=1;76                             int d=u,e=v;77                             while(d!=-1)78                             {79                                 int t=cx[d];80                                 cx[d]=e; cy[e]=d;81                                 d=pre[d]; e=t;82                             }83                         }84                     }85                 }86                 qs++;87             }88             if(cx[i]!=-1) ans++;89         }90     }91 }92 void init()93 {94     memset(bmap,0,sizeof(bmap));95 }
View Code

Hopcroft-Karp算法模版:有时候顶点较多,所以直接用邻接表,没用邻接矩阵。

 1 const int N=10150,INF=0x3f3f3f3f; 2 int cx[N],cy[N],dx[N],dy[N]; 3 bool bmask[N]; 4 int nx,ny,dis,ans; 5 vector<int> bmap[N]; 6 bool searchpath() 7 { 8     queue<int> q; 9     dis=INF;10     memset(dx,-1,sizeof(dx));11     memset(dy,-1,sizeof(dy));12     for(int i=1;i<=nx;i++)13     {14         if(cx[i]==-1){ q.push(i); dx[i]=0; }15         while(!q.empty())16         {17             int u=q.front(); q.pop();18             if(dx[u]>dis) break;19             for(int i=0;i<bmap[u].size();i++)20             {21                 int v=bmap[u][i];22                 if(dy[v]==-1)23                 {24                     dy[v]= dx[u] + 1;25                     if(cy[v]==-1) dis=dy[v];26                     else27                     {28                         dx[cy[v]]= dy[v]+1;29                         q.push(cy[v]);30                     }31                 }32             }33         }34     }35     return dis!=INF;36 }37 int findpath(int u)38 {39     for(int i=0;i<bmap[u].size();i++)40     {41         int v=bmap[u][i];42         if(!bmask[v]&&dy[v]==dx[u]+1)43         {44             bmask[v]=1;45             if(cy[v]!=-1&&dy[v]==dis) continue;46             if(cy[v]==-1||findpath(cy[v]))47             {48                 cy[v]=u; cx[u]=v;49                 return 1;50             }51         }52     }53     return 0;54 }55 void maxmatch()56 {57     ans=0;58     memset(cx,-1,sizeof(cx));59     memset(cy,-1,sizeof(cy));60     while(searchpath())61     {62         memset(bmask,0,sizeof(bmask));63         for(int i=1;i<=nx;i++)64             if(cx[i]==-1) ans+=findpath(i);65     }66 }67 void init()68 {69     for(int i=0;i<N;i++) bmap[i].clear();70 }
View Code

 

题目类型:

1、判断是否构成二分图  http://www.cnblogs.com/Potato-lover/p/3985869.html

2、二分 最大匹配 http://www.cnblogs.com/Potato-lover/p/3989675.html

3、奇偶建图  http://www.cnblogs.com/Potato-lover/p/3991533.html

 

二、最佳匹配

与最大匹配不同的是,最佳匹配是在每一个匹配之间加一个权值。求的就是权值之和最大的匹配。

Kuhn Munkras 算法:时间复杂度为O(m*m*n)

模版:

 1 const int N=110, INF=0x3f3f3f3f; 2 int Map[N][N],mat1[N],mat2[N]; 3 int KM(int m,int n) 4 { 5     int s[N],t[N],a[N],b[N]; 6     int i,j,k,p,q,ans=0; 7     for(i=0;i<m;i++) 8     { 9         a[i]=-INF;10         for(j=0;j<n;j++)11             a[i]=Map[i][j]>a[i]?Map[i][j]:a[i];12         if(a[i]==-INF) return -1;//cannot match13     }14     memset(b,0,sizeof(b));15     memset(mat1,-1,sizeof(mat1));16     memset(mat2,-1,sizeof(mat2));17     for(i=0;i<m;i++)18     {19         memset(t,-1,sizeof(t));20         p=q=0;21         for(s[0]=i;p<=q&&mat1[i]<0;p++)22         {23             for(k=s[p],j=0;j<n&&mat1[i]<0;j++)24             {25                 if(a[k]+b[j]==Map[k][j]&&t[j]<0)26                 {27                     s[++q]=mat2[j]; t[j]=k;28                     if(s[q]<0)29                         for(p=j;p>=0;j=p)30                         {31                             mat2[j]=k=t[j];p=mat1[k]; mat1[k]=j;32                         }33                 }34             }35         }36         if(mat1[i]<0)37         {38             i--,p=INF;39             for(k=0;k<=q;k++)40             {41                 for(j=0;j<n;j++)42                     if(t[j]<0&&a[s[k]]+b[j]-Map[s[k]][j]<p)43                         p=a[s[k]]+b[j]-Map[s[k]][j];44             }45             for(j=0;j<n;j++) b[j]+=t[j]<0?0:p;46             for(k=0;k<=q;k++) a[s[k]]-=p;47         }48 }49     for(i=0;i<m;i++) ans+=Map[i][mat1[i]];50     return ans;51 }52 void init()53 {54     for(int i=0;i<N;i++)55         for(int j=0;j<N;j++)56              Map[i][j]=-INF;57 }
View Code

题目:

 http://www.cnblogs.com/Potato-lover/category/615224.html

 

三、多重匹配

最大匹配值允许A集合与B集合是一一对应。多重匹配可以使得B的元素与多个A的元素匹配。

算法模版:

 1 const int N=1010; 2 int cy[N][N],vcy[N]; 3 bool bmask[N],bmap[N][N]; 4 int nx,ny,limit,L,R; 5 bool findpath(int u) 6 { 7     int i,j; 8     for(i=1;i<=ny;i++) 9     {10         if(bmap[u][i]&&!bmask[i])11         {12             bmask[i]=1;13             if(vcy[i]<limit)14             {15                 cy[i][++vcy[i]] =u;16                 return 1;17             }18             for(j=1;j<=vcy[i];j++)19             {20                 if(findpath(cy[i][j]))21                 {22                     cy[i][j]=u;23                     return 1;24                 }25             }26         }27     }28     return 0;29 }30 bool mulmatch()31 {32     memset(vcy,0,sizeof(vcy));33     for(int i=1;i<=nx;i++)34     {35         memset(bmask,0,sizeof(bmask));36         if(!findpath(i)) return 0;37     }38     return 1;39 }40 void init()41 {42     memset(bmap,0,sizeof(bmap));43 }44 int main()45 {46     L=0,R=nx;47     while(L<=R)48     {49         limit=(L+R)/2;50         if(mulmatch()) R=limit-1;51         else L=limit+1;52     }53     printf("%d\n",L);54 }
View Code

 题目:

2289  Jamie‘s Contact Groups

 

四、二分图模型的应用

1、二分图最小点覆盖  http://www.cnblogs.com/Potato-lover/p/3980411.html

2、有向无环图的最小路径覆盖  http://www.cnblogs.com/Potato-lover/p/3989625.html

3、二分图的最大独立集  http://www.cnblogs.com/Potato-lover/p/3980491.html

 

 

二分匹配