CodeForces-776D The Door Problem

                                                   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的数据出水,误人啊。。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值