并查集的应用(二)

  这里继续讨论一下并查集的应用。

  3 描述
        Dennis的拉面馆隆重开张后,虽然生意很好,但是由于Dennis相信“没有最好,

只有更好”,于是他带领着他的徒弟光光和娃娃一起来到拉面的故乡——甘肃的兰州

,寻找传说中世界上最好吃的拉面……

         在当地人的指导下,Dennis 一行来到了一个偏僻的小村子,传说这个村子就

盛产世界上最好吃的拉面。可是Dennis很快发现了这个村子的村民的奇特之处:

这些村名喜欢说谎话。由于完全说谎话很容易会被识破,所以他们会有时说真话而、

有时说假话,好在经过调查,光光已经知道了村民的这个习俗。为了获得传说中美

味拉面的神秘配方,Dennis精心设计了n个问题,每个问题只需回答“是”或“否”就行,

然后根据答案,他就能得到拉面的秘方。于是光光和娃娃问了m个不同的村民,对

于每个村民,从n个问题中挑出2个不同的问题问他,并纪录下答案交予Dennis统计。

可是Dennis发现这样的统计太复杂和困难了,于是他想知道根据村名的回答,n个

问题的答案可能有的情况总数。


输入格式

      第一行是两个整数n(1<=n<=200),m(0<=m<=10000),分别表示Dennis

准备了n个问题,问了m个不同的村名。接下去m行,第i+1行有四个整数a,b,c,

d(1<=a,c<=n),表示第i个村民对第a个问题的答案是b,对第c个问题的答案是

d。当b或d是0时表示答案为“否”,是1时表示答案为“是”。一个村民在回答时不会

中途改变其说谎性,即他要么一直说真话,要么一直说假话。但我们不知道他到底

在说真话还是假话。


输出格式

      一个整数,表示可能的情况总数。如果不可能,则输出“No Answer”


样例1
样例输入1

2 2
1 1 2 0
1 1 2 1
样例输出1

No Answer

样例2
样例输入2

4 4
1 1 2 1
1 1 3 0
2 1 4 1
3 1 4 0
样例输出2

2


解题思路:

       本题的解题思路类似食物链,将回答相同的放在同一组,回答不同的放在不

同组。即用 MAX_N+N 个元素建立并查集,并查集里的每一个组表示组内所有元

素代表的问题都同时回答正确或回答不正确。然后在查找前N有多少组,再计算问

题答案的可能情况种数。注意,由于最后数据很大,要用到高精度。


参考代码:

#include<stdio.h>
#include<string.h>

const int MAXN=201;   //问题数目 
const int N=1000;     //回答人数 

int par[MAXN+N+10],visit[MAXN],a[1000],c[1000];

int Find(int x)
{
  if (!par[x]) return x;
  return par[x]=Find(par[x]);
}
void unit(int x ,int y)
{
   x=Find(x); y=Find(y);
   if (x!=y)
    par[x]=y;
}

int main()
{
  int cnt,i,j,n,m,l,x1,y1,x2,y2;
  scanf("%d%d",&n,&m);

  for (i=1;i<=m;++i)
  {
    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);

    if (y1==y2){  //回答相同,同时为是或同时为否 
       if (Find(x1)==Find(x2+N)){
        printf("No Answer\n");
        return 0;
       }
       unit(x1,x2);
       unit(x1+N,x2+N);
    }
    else{    //回答不相同 
        if (Find(x1)==Find(x2)) {
            printf("No Answer\n");
            return 0;
        }
        else{
            unit(x1,x2+N);
            unit(x1+N,x2);
        }
    }
  }

  cnt=0;   //查找n个问题一共有多少组 
  for (i=1;i<=n;++i)
  {
    if (!visit[Find(i)]&&(Find(i)<=n))
    {
        ++cnt;
        visit[Find(i)]=1;
    }
  }

 
   //高精度计算问题可能情况种数 
  a[0]=1; l=1;
  for (i=0;i<cnt;++i)
  {
    for (j=0;j<l;++j)
    {
      c[j]+=a[j]*2;
      c[j+1]+=c[j]/10;
      c[j]%=10;
    }
    ++l;
    while (l>=0&&c[l]==0) --l;
     ++l;

    for (j=0;j<l;++j) a[j]=c[j];
    memset(c,0,sizeof(c));
  }

   for (i=l-1;i>=0;--i)  printf("%d",a[i]); 

   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值