挑战NPC

前言

这是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"); } }

注意

由于我们需要输出方案
所以要先从代表球的点搞不然只能答案正确方案错误!!!

你可能感兴趣的:(挑战NPC)