[WC2016]挑战NPC

来源:互联网 发布:网络热敏打印机 编辑:程序博客网 时间:2024/04/26 14:45

前言

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

注意

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

1 0
原创粉丝点击