[学习笔记]Hopcroft-karp 算法

壹、模板测试链接

传送门

贰、说明

在某个地方看到这种可以在 \(\mathcal O(|E|\sqrt{|V|})\) 时间复杂度以内解决问题的方法,感觉挺有意思的......但是似乎只在无权二分图有用?

\(\tt Hopcroft-Karp\) 算法使用 \(\tt BFS\) 来找出多条不相交最短增广路,形成极大增广路集,然后用匈牙利多路增广

\(\tt BFS\) 要找最短的增广路,是因为增广路的长度能增加,他的匹配数也能增加,所以最短保证答案的准确。每一阶段找出来的最短增广路都是相等的长度,后面越来越长。也就是说,每次找出可以达到同样长度的多条增广路。

其实多路增广的思路与最大流的 \(\tt dinic\) 是一样的。

假设我们的图为 \(G=(X,Y,E)\) 算法流程如下:

  1. 使用 \(\tt BFS\),从尚未匹配的 \(X\) 集中的点出发,并在此过程中得到最短增广路长度;
  2. 使用匈牙利算法,将所有满足 \(dis\_y_v=dis\_x_u+1\) 的边进行增广;
  3. 重复 \(1\),直到无法找到增广路;

叁、代码

using namespace Elaina;

const int maxn=500;
const int maxm=5e4;
const int inf=0x3f3f3f3f;

struct edge{int to,nxt;}e[maxm+5];
int tail[maxn+5],ecnt;

int x,y,m;


inline void add_edge(const int u,const int v){
    e[++ecnt]=edge{v,tail[u]};tail[u]=ecnt;
}

inline void input(){
    x=readin(1),y=readin(1),m=readin(1);
    int u,v;
    rep(i,1,m){
        u=readin(1),v=readin(1);
        add_edge(u,v);
    }
}

int xtoy[maxn+5],ytox[maxn+5];
int dx[maxn+5],dy[maxn+5];
queue<int>Q;
// the length of the shortest augmented road
int dis;
/** @return whether have found the augmented road*/
inline int bfs(){
    dis=inf;
    memset(dx+1,-1,x<<2);memset(dy+1,-1,y<<2);
    while(!Q.empty())Q.pop();
    rep(i,1,x)if(!xtoy[i])Q.push(i),dx[i]=0;
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        // ensure the augmented road is the shortest
        if(dx[u]>dis)break;
        for(int i=tail[u],v;i;i=e[i].nxt){
            v=e[i].to;
            if(dy[v]==-1){
                dy[v]=dx[u]+1;
                // have found the shortest augmented road
                if(!ytox[v])dis=dy[v];
                else dx[ytox[v]]=dy[v]+1,Q.push(ytox[v]);
            }
        }
    }
    return dis!=inf;
}

int vis[maxn+5];
int dfs(const int u){
    for(int i=tail[u],v;i;i=e[i].nxt)if(!vis[v=e[i].to]){
        if(dy[v]==dx[u]+1){
            vis[v]=1;
            // length limited
            if(ytox[v] && dy[v]==dis)continue;
            if(!ytox[v] || dfs(ytox[v])){
                ytox[v]=u,xtoy[u]=v;
                return 1;
            }
        }
    }
    return 0;
}

inline int HK(){
    int ret=0;
    while(bfs()){
        memset(vis+1,0,y<<2);
        rep(i,1,x)if(!xtoy[i] && dfs(i))
            ++ret;
    }
    return ret;
}

signed main(){
    input();
    writc(HK(),'\n');
    return 0;
}
posted @ 2021-02-04 11:36  Arextre  阅读(224)  评论(0编辑  收藏  举报