二分图最大匹配(模板+题目)

定义和定理:

最大匹配数:最大匹配的匹配边的数目
最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择
最大独立数:选取最多的点,使任意所选两点均不相连
最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。

定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)
定理2: 最大独立数与最小点覆盖数互补
定理3:最小路径覆盖数 = 顶点数 - 最大匹配数

匈牙利算法:
模板一:

/* **************************************************************************
//二分图匹配(匈牙利算法的dfs实现)
//初始化:g[][]两边顶点的划分情况
//建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配
//g没有边相连则初始化为0
//uN是匹配左边的顶点数,vN是匹配右边的顶点数,要记得初始化
//调用:res=hungary();输出最大匹配数
//优点:适用于稠密图,DFS找增广路,实现简洁易于理解
//时间复杂度:O(VE)
//***************************************************************************/
//顶点编号从0开始的
const int maxn=510;
int uN,vN;//u,v数目
int g[maxn][maxn];
int link[maxn];
bool used[maxn];
bool dfs(int u){//从左边开始找增广路径
    int v;
    for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改
      if(g[u][v]&&!used[v])
      {
          used[v]=true;
          if(link[v]==-1||dfs(link[v]))
          {//找增广路,反向
              link[v]=u;
              return true;
          }
      }
    return false;
}

int hungary(){
    int res=0;
    int u;
    memset(link,-1,sizeof(link));
    for(u=0;u<uN;u++)
    {
        memset(used,0,sizeof(used));
        if(dfs(u)) res++;
    }
    return res;
}
//******************************************************************************/

模板二:

/*
例题HDU 1054
用STL中的vector建立邻接表实现匈牙利算法,要记得初始化! ! !效率比较高
 G++  578ms  580K
*/

const int maxn=1505;//这个值要超过两边个数的较大者,因为有link
int link[maxn];
bool used[maxn];
vector<int>G[maxn];
int uN;
bool dfs(int u){
  for(int i=0;i<G[u].size();i++){
    if(!used[G[u][i]]){
      used[G[u][i]]=true;
      if(link[G[u][i]]==-1||dfs(link[G[u][i]])){
        link[G[u][i]]=u;
        return true;
      }
    }
  }
  return false;
}

int hungary(){
  int u;
  int res=0;
  memset(link,-1,sizeof(link));
  for(u=0;u<uN;u++){
    memset(used,false,sizeof(used));
    if(dfs(u)) res++;
  }
  return res;
}

 Hopcroft-Carp算法
HC算法的原理:

它可以做到O(sqrt(V)*E)的时间复杂度,并且在实际使用中效果不错而且算法本身并不复杂。

     Hopcroft-Karp算法是Hopcroft和Karp在1972年提出的,该算法的主要思想是在每次增广的时候不是找一条增广路而是同时找几条不相交的最短增广路,形成极大增广路集,随后可以沿着这几条增广路同时进行增广。

     可以证明在寻找增广路集的每一个阶段所寻找到的最短增广路都具有相等的长度,并且随着算法的进行最短增广路的长度是越来越长的,更进一步的分析可以证明最多只需要增广ceil(sqrt(n))次就可以得到最大匹配(证明在这里略去)。

     因此现在的主要难度就是在O(e)的时间复杂度内找到极大最短增广路集,思路并不复杂,首先从所有X的未盖点进行BFS,BFS之后对每个X节点和Y节点 维护距离标号,如果Y节点是未盖点那么就找到了一条最短增广路,BFS完之后就找到了最短增广路集,随后可以直接用DFS对所有允许弧 (dist[y]=dist[x]+1,可以参见高流推进HLPP的实现)进行类似于匈牙利中寻找增广路的操作,这样就可以做到O(m)的复杂度。

模板:

//这个算法比匈牙利算法的时间复杂度要小,大数据可以采用这个算法

/* *********************************************
二分图匹配(Hopcroft-Carp的算法)。
初始化:g[][]邻接矩阵
调用:res=MaxMatch();  Nx,Ny要初始化!!!
时间复杂大为 O(sqrt(V)*E)

适用于数据较大的二分匹配
需要queue头文件
********************************************** */
const int maxn=3000;
const int inf=1<<28;
int g[maxn][maxn],Mx[maxn],My[maxn],Nx,Ny;
int dx[maxn],dy[maxn],dis;
bool vis[maxn];

bool searchP(){
    queue<int>Q;
    dis=inf;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=0;i<Nx;i++)
        if(Mx[i]==-1){
            Q.push(i);
            dx[i]=0;
        }
    while(!Q.empty()){
        int u=Q.front();
        Q.pop();
        if(dx[u]>dis)  break;
        for(int v=0;v<Ny;v++)
            if(g[u][v]&&dy[v]==-1){
                dy[v]=dx[u]+1;
                if(My[v]==-1)  dis=dy[v];
                else
                {
                    dx[My[v]]=dy[v]+1;
                    Q.push(My[v]);
                }
            }
    }
    return dis!=inf;
}

bool dfs(int u){
    for(int v=0;v<Ny;v++)
       if(!vis[v]&&g[u][v]&&dy[v]==dx[u]+1)
       {
           vis[v]=1;
           if(My[v]!=-1&&dy[v]==dis) continue;
           if(My[v]==-1||dfs(My[v]))
           {
               My[v]=u;
               Mx[u]=v;
               return 1;
           }
       }
    return 0;
}

int MaxMatch(){
    int res=0;
    memset(Mx,-1,sizeof(Mx));
    memset(My,-1,sizeof(My));
    while(searchP())
    {
        memset(vis,0,sizeof(vis));
        for(int i=0;i<Nx;i++)
          if(Mx[i]==-1 && dfs(i))  res++;
    }
    return res;
}




  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值