URAL1099-Work Scheduling(一般图最大匹配(带花树))

题目来源:https://et/problem/URAL-1099

题意

给出一个一般有向图,求最大匹配,并且把最大匹配的点数输出,以及匹配额点。。。

思路

最大匹配分为二分图最大匹配和一般图最大匹配,利用匈牙利算法或者HK算法可解,一般图最大匹配利用带花树算法可解(缩花,开花)(并查集证明是同一朵花,最近公共祖先找花。)
在增广路径的过程中,利用深搜的原理生成搜索树,假如称离根节点有偶数条边的点为偶点,称离根节点有奇数条边的点为奇点,那么假设已匹配点称为S,待匹配点称为T,也即是说偶点是S,奇点是T:
对于二分图来讲:
递归找到一个T,便可以增广路径,也即是S–>T(S可以到T),也就是偶点到奇点。
对于一个一般图来讲:
递归找到一个T,同样可以增广路径,但是他还有另外一种情况:偶点到偶点,也就是一个点出现两次,并且距离都是偶数条边,如果在这棵树上两个偶点连一条边,那么从树根到这两个偶点里面的任意一个点都可以是偶点(就成了花),也就是这一般图中,所覆盖的点可以整体看成一个偶点(缩花),利用并查集,以及LCA操作进行缩花,然后就会清除这种偶点到偶点的情况。。。
推荐博客:http://www.csie.ntnu.edu.tw/~u91029/

代码

//裸模板
#include
#include
#include
#include
#include
using namespace std;
const int maxn=230;
int n;
int pre[maxn];
int finds(int x)
{
    return pre[x]==x?x:pre[x]=finds(pre[x]);
}

void unit(int x,int y)
{
    int fx=finds(x);
    int fy=finds(y);
    if(fx!=fy)
        pre[fx]=fy;
}
int match[maxn],nexx[maxn],vis[maxn],mark[maxn],Q[maxn],rear;
int LCA(int x,int y)
{
    static int t=0;t++;
    while(1)
    {
        if(x!=-1)
        {
            x=finds(x);
            if(vis[x]==t)//如果把t直接改成1,就会超时。。不知道是为啥
                return x;
            vis[x]=t;
            if(match[x]!=-1) x=nexx[match[x]];
            else x=-1;
        }
        swap(x,y);
    }
}
void group(int x,int y)
{
    while(x!=y)
    {
        int a=match[x],b=nexx[a];
        if(finds(b)!=y) nexx[b]=a;
        if(mark[a]==2) mark[Q[++rear]=a]=1;
        if(mark[b]==2) mark[Q[++rear]=b]=1;
        unit(x,a);
        unit(a,b);
        x=b;
    }
}
vector<int> v[maxn];
void ex_load(int s)
{
    memset(vis,0,sizeof(vis));
    memset(mark,0,sizeof(mark));
    memset(nexx,-1,sizeof(nexx));
    for(int i=1;i<=n;i++) pre[i]=i;
    mark[s]=1;
    Q[0]=s,rear=0;
    for(int front=0;match[s]==-1&&front<=rear;front++)
    {
        int x=Q[front];

        for(int i=0;i<(int)v[x].size();i++)
        {

            int y=v[x][i];
            if(match[x]==y) continue;
            if(finds(x)==finds(y)) continue;
            if(mark[y]==2) continue;
            if(mark[y]==1)
            {
                int r=LCA(x,y);
                if(finds(x)!=r) nexx[x]=y;
                if(finds(y)!=r) nexx[y]=x;
                group(x,r);
                group(y,r);
            }
            else if(match[y]==-1)
            {
                nexx[y]=x;
                int u=y;
                while(u!=-1)
                {
                   int v=nexx[u];
                   int tmp=match[v];
                   match[u]=v,match[v]=u;
                   u=tmp;
                }
                break;
            }
            else
            {
                nexx[y]=x;
                mark[Q[++rear]=match[y]]=1;
                mark[y]=2;
            }
        }
    }
}
bool g[maxn][maxn];
int main()
{
    scanf("%d",&n);
    memset(g,0,sizeof(g));
    int x,y;
    while(~scanf("%d%d",&x,&y))
    {
        if(x!=y&&!g[x][y])
        {
            v[x].push_back(y);
            v[y].push_back(x);
            g[x][y]=g[y][x]=1;
        }
    }
    memset(match,-1,sizeof(match));
    for(int i=1;i<=n;i++)
    {
        if(match[i]==-1)
            ex_load(i);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(match[i]!=-1)
            ans++;
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;i++)
    {
        if(match[i]>i)
        {
            printf("%d %d\n",i,match[i]);
        }
    }
    return 0;
}

你可能感兴趣的:(ACM竞赛,【图论】--二分图匹配,ACM的进程)