A bug's life 种类并查集带权并查集

  我是先做了种类并查集的题,后来做了带权并查集的题,最后发现,种类并查集和带权并查集好像真的差不多,我现在已经怀疑是包含关系了,即带权并查集包含种类并查集。关于带权并查集。请允许我很朴素的说下我的愚见,带权了嘛,就是某个数组带了某些权力。而这些权力,决定了怎么查和并。就说这个种类并查集吧,这里的权就是不同种类直接的权,比如这道可能是最简单的种类并查集,a bug's life,就只有两个种类,雄的,雌的。需要判断的也只有是否种类相同。像我下一章博客要做的食物链,就有三个物种,也不是简单的判断是否属于同种了。

做任何事,也都是从简单起手嘛,所以我先分析一下a bug's life(可能有分析的不太对的地方,欢迎大家指证

           先把题目移过来吧嗯~ 

 

Background 
Professor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and that they only interact with bugs of the opposite gender. In his experiment, individual bugs and their interactions were easy to identify, because numbers were printed on their backs. 
Problem 
Given a list of bug interactions, decide whether the experiment supports his assumption of two genders with no homosexual bugs or if it contains some bug interactions that falsify it.

Input

The first line of the input contains the number of scenarios. Each scenario starts with one line giving the number of bugs (at least one, and up to 2000) and the number of interactions (up to 1000000) separated by a single space. In the following lines, each interaction is given in the form of two distinct bug numbers separated by a single space. Bugs are numbered consecutively starting from one.

Output

The output for every scenario is a line containing "Scenario #i:", where i is the number of the scenario starting at 1, followed by one line saying either "No suspicious bugs found!" if the experiment is consistent with his assumption about the bugs' sexual behavior, or "Suspicious bugs found!" if Professor Hopper's assumption is definitely wrong.

Sample Input

2
3 3
1 2
2 3
1 3
4 2
1 2
3 4

Sample Output

Scenario #1:
Suspicious bugs found!

Scenario #2:
No suspicious bugs found!

先解释一下题意,虽然可能都知道什么意思。科学家什么的故事背景我就不bb了,先给一个t,表示需要测试的数据。随后给t个测试数据,每个测试数据会现有一排n,m表示有n只虫子,m表示发生了m次关系。随后又有m排关系。最后让你判断有哪些虫子发生错了关系,也就是问有那几组虫子的性取向有问题,在这道题看来,同性恋就是性取向有问题。虽然我们都认为,同性恋应该被接受(●'◡'●)。

种类查集一般都会开两个数组,一个表示其父亲节点,我习惯用pre[]来表示,另一个呢,就表示的是种类是否相同,在这道题里 我习惯用sexual[]来表示。要么同性,要么异性,只有两种状态,所以可以用bool来定义sexual[]。当sexual[]为true或1时,表示异性,false或0时表示同性.那么sexual[]表示和谁同异性呢?假如有pre[i]=x,sexual[i]=1,就表明i和他的祖宗是异性的,就是说i和x是异性的。为什么能想到用这种方法来表示关系呢?。。。谁知道呢~~~可能这就是种类并查集的方法吧。当然这道题,也是可以不这么做的。在后面会给出另一种做法。

首先,给出查的find函数

 

int find(int x)
{
    if(pre[x]==x) return x;
    else{
        int t=find(pre[x]);//寻找父亲节点
        sexual[x]=(sexual[x]+sexual[pre[x]])&1;//关系更新很重要。
        pre[x]=t;//路径压缩
        return pre[x];
    }
}

 sexual[x]=(sexual[x]+sexual[pre[x]])&1;                                                                                                                   sexual[x]=(sexual[x]+sexual[pre[x]])%2                                                                                                                               上面的两个式子意思是一样的,只不过&1比%2的时间复杂度要小。为什么能写出这样的式子呢?给大家列一个表格

 

 

Pre[x]

1

1

0

0

x

1

0

1

0

Sexual[x]

0

1

1

0

这样相信还是蛮清晰的吧。

接下来就是并的操作了,我习惯用join()函数来表示

 

void join(int a,int b)
{
    int fa=find(a);
    int fb=find(b);
    if(fa==fb){ //如果父亲点相同,只需要判断a和b的sexual是否相同就ok了
        if(sexual[a]==sexual[b]) flag=1;//注意了,sexual[x]是判断x和x的祖先是否同性,而flag是判断a的祖先和b的祖先是否同性
    }
    else {//如果两个的父亲节点不相同,即还没有合并过。这里是把a并到b里,反过来也一样。
        pre[fa]=fb;
        sexual[fa]=(sexual[a]+sexual[b]+1)&1;//合并,关系转换
    }
}

sexual[fa]=(sexual[a]+sexual[b]+1)&1;这个式子又是怎么来的?关于这个,只需要把例子过一遍就会明白的了。还是蛮好懂得,虽然我还是看了好久/(ㄒoㄒ)/~~菜哭
下面附上完整的代码:
 

#include<stdio.h>
int n,m;
int pre[10001];
bool sexual[10001];
bool flag;
void standard()
{  flag=0;
    for(int i=1;i<=n;i++){
        pre[i]=i;
        sexual[i]=0;
    }
}
int find(int x)
{
    if(pre[x]==x) return x;
    else{
        int t=find(pre[x]);//寻找父亲节点
        sexual[x]=(sexual[x]+sexual[pre[x]])&1;//关系更新很重要
        pre[x]=t;//路径压缩
        return pre[x];
    }
}
void join(int a,int b)
{
    int fa=find(a);
    int fb=find(b);
    if(fa==fb){//如果父亲点相同,只需要判断a和b的sexual是否相同就ok了
        if(sexual[a]==sexual[b]) flag=1;//注意了,sexual[x]是判断x和x的祖先是否同性,而flag是判断a的祖先和b的祖先是否同性
    }
    else {//如果两个的父亲节点不相同,即还没有合并过。这里是把a并到b里,反过来也一样。
        pre[fa]=fb;
        sexual[fa]=(sexual[a]+sexual[b]+1)&1;
    }
}
int main()
{
    int t,k=0,x,y;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        standard();
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            join(x,y);
        }
        if(flag) printf("Scenario #%d:\nSuspicious bugs found!\n\n",++k);
        else printf("Scenario #%d:\nNo suspicious bugs found!\n\n",++k);
    }
    return 0;
}

上面提到的另一种方法如下:

 

#include<stdio.h>
int pre[4050];
int x,y,n,m;
void standard()
{
    for(int i=1;i<=2*n;i++){
        pre[i]=i;
    }
}
int find(int x)
{
    if(x!=pre[x])
        pre[x]=find(pre[x]);
    return pre[x];
}
int main()
{
    int t,k=0;
    scanf("%d",&t);
    while(t--){
            int flag=0;
        scanf("%d%d",&n,&m);
        standard();
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            int fx=find(x);
            int fy=find(y);
            if(fx==fy) {flag=1;continue;}
            pre[fx]=find(y+n);
            pre[fy]=find(x+n);
        }
        if(!flag) printf("Scenario #%d:\nNo suspicious bugs found!\n\n",++k);
     else printf("Scenario #%d:\nSuspicious bugs found!\n\n",++k);
    }
        return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值