二分图匹配之匈牙利算法

PART 1 什么是二分图

二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。

此图即为一个二分图

PART 2 什么是二分图匹配

给定一个二分图G,在G的一个子图M中,M的边集{E}中的任意两条边都不依附于同一个顶点,则称M是一个匹配。

极大匹配(Maximal Matching)是指在当前已完成的匹配下,无法再通过增加未完成匹配的边的方式来增加匹配的边数。最大匹配(maximum matching)是所有极大匹配当中边数最大的一个匹配。选择这样的边数最大的子集称为图的最大匹配问题。
如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。
求二分图最大匹配可以用最大流(Maximal Flow)或者匈牙利算法(Hungarian Algorithm)
PART 3 匈牙利算法
匈牙利算法算法的主要操作就是枚举左边的点,找它第一个与右面点相连的边,然后如果所连点已经与其它点匹配过,则将之前的点与其它与其相连的点匹配,并不断重复此操作,如果之前点不能与其它点匹配则当前枚举的点枚举其下一条边,如果所有边都不行则此点不参与匹配
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<vector>
using namespace std;
int g[1100][1100];
int used[1100],wh[1100];
int ans;
int t,n,m;
inline bool work(int now){
    int i,j,k;
    for(j=1;j<=m;j++)
       if(used[j]!=t&&g[now][j]){
           used[j]=t;
           if(!wh[j]||work(wh[j])){
               wh[j]=now;
               return 1;
           }
    }
    return 0;
}
inline void go(){
    int i,j,k;
    for(i=1;i<=n;i++){
        t=i;
        if(work(i))ans++;
    }
}
int main(){
    int i,j,k,x,y;
    cin>>n>>m>>k;
    for(i=1;i<=k;i++){
        cin>>x>>y;
        if(y<=m)
          g[x][y]=1;
    }
    go();
    cout<<ans<<endl;
    return 0;
}
附赠最大流做法代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
const int inf=1e9+7;
struct edge{
      int c,to,next;
}e[2000000];
int head[5000],level[5000],cnt;
void read(int &x){
      int f=1;x=0;
      char s=getchar();
      while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
      while(s>='0'&&s<='9'){x=x*10+(s-'0');s=getchar();}
      x=(f==1?x:-x);
}
void add(int u,int v,int w){
      e[cnt].c=w;
      e[cnt].to=v;
      e[cnt].next=head[u];
      head[u]=cnt++;
      e[cnt].to=u;
      e[cnt].next=head[v];
      head[v]=cnt++;
}
int bfs(int s,int t){
      queue<int>q;
      memset(level,-1,sizeof(level));
      q.push(s);
      level[s]=0;
      while(!q.empty()){
          int u,v;
          u=q.front();
          q.pop();
          for(int i=head[u];~i;i=e[i].next){
              v=e[i].to;
              if(level[v]==-1&&e[i].c){
                  level[v]=level[u]+1;
                  q.push(v);
                  if(v==t)return 1;
              }
          }
      }
      if(level[t]==-1)return 0;
      return 1;
}
int dfs(int u,int v,int flow){
      if(u==v)return flow;
      int res=0;
      for(int i=head[u];~i;i=e[i].next){
          int j=e[i].to;
          if(level[j]==level[u]+1&&e[i].c){
              int f=dfs(j,v,min(e[i].c,flow-res));
              res+=f;
              e[i].c-=f;
              e[i^1].c+=f;
          }
      }
      if(!res)level[u]=-1;
      return res;
}
int main()
{     int n,m,E,i,j,k,u,v,ans=0;
      read(n);
      read(m);
      read(E);
      memset(head,-1,sizeof(head));
      for(i=1;i<=E;i++){
          read(u);
          read(v);
          if(v>m)continue;
          add(u,v+n,1);
      }
      int s=n+m+1,t=n+m+2;
      for(i=1;i<=n;i++){
         add(s,i,1);
      }
      for(i=1;i<=m;i++){
         add(n+i,t,1);
      }
      while(bfs(s,t))
         ans+=dfs(s,t,inf);
      printf("%d\n",ans);
      return 0;
}

 

posted @ 2018-05-09 19:38  水题收割者  阅读(204)  评论(0编辑  收藏  举报