题目来源: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<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
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;
}