一般图匹配
建图很巧妙。将一个筐子拆成3个点都连球,并且这三个点也连成一个环,然后跑一般图最大匹配。我们看和球相连的匹配,一定是n,这样一来就会最大化筐子的三元环的匹配数。 然而筐子的环上能匹配就意味着装不超过一个球(有两个点没有匹配上)。
给带花树打了一点注释
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define N 305
#define M 105
#define P 800
#define add2(a,b) {add(a,b);add(b,a);}
using namespace std;
map<int,int> pos;
struct edge{int next,to;}e[300000];
int ecnt=0, mat[P], q[P], pre[P], sta[P], fa[P], vis[P], timer, head, tail, last[P], id[M][3], pcnt;
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void add(int a, int b)
{
e[++ecnt]=(edge){last[a],b};
last[a]=ecnt;
}
int lca(int x, int y)//暴力找LCA
{
++timer;
for(x=find(x), y=find(y);;swap(x,y))
{
if(x)
{
if(vis[x]==timer)return x;
vis[x]=timer;
x=find(pre[mat[x]]);
}
}
}
void blossom(int root, int x, int y)
{
for(int m ;find(x)!=root;y=m, x=pre[y])
{
pre[x]=y;//保证任何时候从花上找到一个未匹配点,都可以沿着匹配边的方向向根更新,此时pre是双向的(pre[x]=y,pre[y]=x)
m = mat[x];
if(sta[m]==1)
{
q[tail++]=m;
sta[m]=0;
}
if(find(x)==x)fa[x]=root;
if(find(m)==m)fa[m]=root;
}
}
bool match(int s)
{
memset(sta,-1,sizeof(sta));
memset(pre,0,sizeof(pre));//pre记录前一个点
for(int i = 1; i <= pcnt; i++)fa[i]=i;
q[0]=s;
sta[s]=0;//起点记为偶点
for(head=0, tail=1; head<tail; head++)
{
int x=q[head];
for(int i = last[x]; i; i=e[i].next)
{
int y=e[i].to;
if(sta[y]==-1)
{
sta[y]=1;
pre[y]=x;
if(!mat[y])
{
for(int mm; y; y=mm, x=pre[y])
{
mm=mat[x];
mat[y]=x;
mat[x]=y;
}
return 1;
}
else
{
q[tail++]=mat[y];
sta[mat[y]]=0;
}
}
else if(sta[y]==0 && find(x)!=find(y))
{
int temp=lca(x,y);
//缩花,两个方向都要缩
blossom(temp,x,y);
blossom(temp,y,x);
}
}
}
return 0;
}
void clr()
{
ecnt=0; timer=0;
memset(last,0,sizeof(last));
memset(mat,0,sizeof(mat));
memset(vis,0,sizeof(vis));
}
void init()
{
pcnt=N;
for(int i = 1; i < M; i++)
for(int j = 0 ; j < 3; j++)
{
id[i][j]=++pcnt;
pos[pcnt]=i;
}
}
int main()
{
int T;
scanf("%d",&T);
init();
while(T--)
{
clr();
int n, m, e, ans=0;
scanf("%d%d%d",&n,&m,&e);
for(int i = 1; i <= e; i++)
{
int a, b;
scanf("%d%d",&a,&b);
for(int j = 0; j < 3; j++)
add2(a,id[b][j]);
}
for(int i = 1; i <= m; i++)
for(int j = 0; j < 3; j++)
add2(id[i][j],id[i][(j+1)%3]);
for(int i = 1; i <= pcnt; i++)
if(!mat[i] && last[i])
match(i);
for(int i = 1; i <= m; i++)
if(mat[id[i][0]]>N || mat[id[i][1]]>N)
ans++;
printf("%d\n",ans);
for(int i = 1; i <= n; i++)
printf("%d ",pos[mat[i]]);
puts("");
}
}