CodeForces-776D The Door Problem

来源:互联网 发布:沈航网络自助平台套餐 编辑:程序博客网 时间:2024/06/01 09:37

                                                   The Door Problem

做这个题做了几天,中间各种事耽搁,不过也总结了很多。

题意:n扇门,初始状态可能开着也可能关着,m个开关,每个开关可以改变若干个门的状态,每扇门只由两个开关控制,问是否存在一种方法把所有的门都打开。

因为做并查集碰到了个问题,于是队友甩给我这道题,说实话很难想到用并查集,看了一下别人思路才知道的。

把门作为边,连接两个开关,如果门是开着,所连的两个开关属于一个集合,同时开关,如果门是关着,两个开关只能选一个,所以属于不同的集合,问题就转化到了求这些开关是否能分成两个集合。

这就是并查集问题了,用类似POJ-1703的方法是MLE,很不明白为什么,因为之前做的是HDU-5971,用的种类并查集却RE了,我还怀疑是递归的问题,可那道题数据量很小,不应该有这样的问题的。做这个题用种类并查集MLE,也是莫名奇妙。

MLE代码:

void init(){    for(int i=1; i<=n; i++) f[i]=p[i]=i;    memset(mp,0,sizeof(mp));    tot=0;}int find(int x){    return f[x]==x?x:f[x]=find(f[x]);}int main(){    while(~scanf("%d%d",&n,&m))    {        init();        for(int i=1; i<=n; i++) scanf("%d",&a[i]);        for(int i=1; i<=m; i++)        {            int k,x;            scanf("%d",&k);            for(int j=1; j<=k; j++)            {                scanf("%d",&x);                if(!mp[x][0]) mp[x][0]=i;                else mp[x][1]=i;            }        }        int flag=0;        for(int i=1; i<=n; i++)        {            int u=mp[i][0],v=mp[i][1];            int f1=find(u),f2=find(v);            if(a[i]) f[f1]=f2;            else            {                if(p[v]==v) p[v]=u;                if(p[u]==u) p[u]=v;                int f3=find(p[u]),f4=find(p[v]);                if(f1!=f4) f[f1]=f4;                if(f2!=f3) f[f2]=f3;            }        }        for(int i=1; i<=n&&!flag; i++)            if(i!=p[i]&&find(i)==find(p[i])) flag=1;        if(flag) puts("NO");        else puts("YES");    }    return 0;}

AC代码:

void init(){    for(int i=1; i<N; i++) f[i]=p[i]=i;    memset(mp,0,sizeof(mp));}int find(int x){    return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){    int f1=find(u),f2=find(v);    f[f1]=f2;}int main(){    while(~scanf("%d%d",&n,&m))    {        init();        for(int i=1; i<=n; i++) scanf("%d",&a[i]);        for(int i=1; i<=m; i++)        {            int k,x;            scanf("%d",&k);            for(int j=1; j<=k; j++)            {                scanf("%d",&x);                if(!mp[x][0]) mp[x][0]=i;                else mp[x][1]=i;            }        }        int flag=0;        for(int i=1; i<=n; i++)        {            int u=mp[i][0],v=mp[i][1];            int f1=find(u),f2=find(v);            int f3=find(u+m),f4=find(v+m);            if(a[i])            {//                f[f1]=f2;                 merge(u,v);                 merge(u+m,v+m);            }            else            {//                 f[f1]=f4;//                 f[f2]=f3;                 merge(u,v+m);                 merge(v,u+m);            }        }        for(int i=1; i<=m&&!flag; i++)            if(find(i)==find(i+m)) flag=1;        if(flag) puts("NO");        else puts("YES");    }    return 0;}

很不明白为什么放在merge函数里分别合并就对了,但直接合并就MEL。队友说直接合并相当于把树压缩了,然后下次查找的时候不断的压栈,导致内存超限,还是不太明白。

不过同一个集合合并的时候也要将其对立合并,这里也不是很明白。

关键就在这个对立问题上,在做POJ-1703的时候,直接用第一份代码的方式就可以AC了,直接将其对立存起来,合并只需将自己与对立的对立合并就行了,但这里涉及到同集合合并问题,用相同的放法就行不通了。如果所有的关系都是对立的也许可以。第二种写法在很多种类并查集的题都可以过,所以总结是POJ/HDU的数据出水,误人啊。。



原创粉丝点击