[模板] 匈牙利算法&&二分图最小字典序匹配

匈牙利算法

简介

匈牙利算法是一种求二分图最大匹配的算法.

时间复杂度: 邻接表/前向星: \(O(n * m)\), 邻接矩阵: \(O(n^3)\).
空间复杂度: 邻接表/前向星: \(O(n + m)\), 邻接矩阵: \(O(n^2)\).

它的主要思路就是对每个点寻找增广路, 尝试改变之前的选择, 判断是否可行.

事实上, 利用dinic/isap跑二分图有 \(O(n * \sqrt{m})\) 的优秀复杂度(不会证), 因此匈牙利算法仅用于少数特殊情况↓

代码

int to[nsz][nsz]; //邻接表
int vi[nsz],mat[nsz];
bool arg(int p){
    rep(i,1,to[0]){
        int v=to[p][i];
        if(vi[v])continue;
        vi[v]=1;
        if(mat[v]==0||arg(mat[v])){
            mat[v]=p,mat[p]=v;
            return 1;
        }
    }
    return 0;
}

int hung(){
    int res=0;
    repdo(i,1,n){
        memset(vi,0,sizeof(vi));
        res+=arg(i);
    }
    return res;
}

二分图最小字典序匹配

简介

这就是上面说的特殊情况:P

考虑匈牙利算法的过程: 将每一个点尝试增广, 同时改变之前的点的匹配.

因此, 我们可以考虑将所有点的出边按标号排序, 逆向遍历每一个点, 并按标号顺序尝试增广.
显然, 第一个点的匹配一定是它能匹配到的最小标号, 第二个点的匹配是满足第一个点时的最小标号, 以此类推.

代码

//[NOI2009] 变换序列
int to[nsz][3];
int vi[nsz],mat[nsz];
bool arg(int p){
    rep(i,0,1){
        int v=to[p][i];
        if(vi[v])continue;
        vi[v]=1;
        if(mat[v]==0||arg(mat[v])){
            mat[v]=p,mat[p]=v;
            return 1;
        }
    }
    return 0;
}

int hung(){
    int res=0;
    repdo(i,n-1,0){
        memset(vi,0,sizeof(vi));
        res+=arg(i);
    }
    return res;
}

参考资料

  • 匈牙利算法(简单易懂)
  • byvoid的题解

转载于:https://www.cnblogs.com/ubospica/p/10249693.html

你可能感兴趣的:([模板] 匈牙利算法&&二分图最小字典序匹配)