做这个题做了几天,中间各种事耽搁,不过也总结了很多。
题意: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的数据出水,误人啊。。