前言
这是WC2016的第一题,在场上迅速能发现60分可做。要分成四个部分。最后由于没发现最多只能放三个球就0分了。还有这题不是NPC问题出题人傻逼
题目大意
给定你e个关系第i个关系表明编号为ai的球可以放到编号为bi的筐子里。每个筐子最多放三个球。请你安排方案,让每个球放进一个筐子里,且所装球数不超过1的箱子数最多。
球的个数n<=3*m,筐子的个数m<=100。
巧妙建模
我们将一个筐子拆为三个点,并让它们连边成为三元环。对于每个球建一个点,然后该点对可以放的筐子的点都连上一条边。现在,只要做一般图最大匹配,答案减去n就是答案。
为什么?
假如一个三元环只有不超过一个匹配点,那么显然就会出现一条匹配边,否则就没有匹配边。因此最大匹配减去球数就是所装球数不超过1的箱子的最大值。
至于一般图最大匹配,要使用带花树算法,UOJ有一道模板题。
参考程序
#include<cstdio>
#include<algorithm>
#include<deque>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=1000+10,maxm=100000+10;
deque<int> dl;
int h[maxn],go[maxm*2],next[maxm*2],fa[maxn],f[maxn],type[maxn],match[maxn],vis[maxn];
int i,j,k,l,t,n,m,e,tot,ans,cnt,ca,wdc;
void add(int x,int y){
go[++tot]=y;
next[tot]=h[x];
h[x]=tot;
}
void link(int x,int y){
add(x,y);add(y,x);
}
int getf(int x){
return f[x]?f[x]=getf(f[x]):x;
}
int lca(int x,int y){
++cnt;
while (x||y){
if (x){
x=getf(x);
if (vis[x]==cnt) return x;
vis[x]=cnt;
x=fa[match[x]];
}
swap(x,y);
}
}
void change(int u,int y){
int v,p;
while (u!=y){
v=match[u],p=fa[v];
if (getf(p)!=y) fa[p]=v;
if (type[v]==2){
type[v]=1;
dl.push_back(v);
}
if (!f[u]) f[u]=y;
if (!f[v]) f[v]=y;
u=p;
}
}
int dfs(int x){
int i,u,v,p;
fo(i,1,wdc) type[i]=f[i]=fa[i]=0;
type[x]=1;
dl.push_back(x);
while (!dl.empty()){
u=dl.front();
dl.pop_front();
t=h[u];
while (t){
v=go[t];
t=next[t];
if (type[v]==2||match[v]==u||getf(u)==getf(v)) continue;
if (type[v]==1){
p=lca(u,v);
if (getf(u)!=p) fa[u]=v;
if (getf(v)!=p) fa[v]=u;
change(u,p);
change(v,p);
}
else{
if (!match[v]){
while (u){
j=fa[match[u]];k=match[u];
match[u]=v;match[v]=u;
v=k;u=j;
}
while (!dl.empty()) dl.pop_front();
return 1;
}
else{
fa[v]=u;
type[v]=2;
type[match[v]]=1;
dl.push_back(match[v]);
}
}
}
}
return 0;
}
int main(){
scanf("%d",&ca);
while (ca--){
tot=0;
cnt=0;
scanf("%d%d%d",&n,&m,&e);
wdc=3*m+n;
fill(vis+1,vis+wdc+1,0);
fill(h+1,h+wdc+1,0);
fill(match+1,match+wdc+1,0);
ans=0;
fo(i,1,m){
link(3*i-3+1,3*i-3+2);
link(3*i-3+2,3*i-3+3);
link(3*i-3+3,3*i-3+1);
}
fo(i,1,e){
scanf("%d%d",&j,&k);
link(3*m+j,3*k-3+1);
link(3*m+j,3*k-3+2);
link(3*m+j,3*k-3+3);
}
fd(i,wdc,1)
if (!match[i]) ans+=dfs(i);
printf("%d\n",ans-n);
fo(i,1,n) printf("%d ",(match[3*m+i]-1)/3+1);
printf("\n");
}
}
注意
由于我们需要输出方案
所以要先从代表球的点搞不然只能答案正确方案错误!!!