【ACM之路】2.并查集

1.问题引出

假设有10个强盗,请问他们一共几伙人?

已知:

1号强盗与2号强盗是一伙,

3号强盗与4号强盗是一伙,

5号2号一伙,

4号6号一伙

2号6号一伙,

8号7号一伙,

9号7号一伙,

1号6号一伙,

2号4号一伙,

请问10个强盗一共是几伙人?

 


 

2.方法思路

1.首先我们先设立一个数组f[11] , 并对其初始化 , 从1号元素到10号元素,分别赋值1,2,3……10。

int f[11];
for(int i=1;i<=10;i++)
{
    f[i] = i;
}

这段代码表示给每个强盗进行编号,1号强盗编码为1,2号强盗编码为2,……。

2.其次,对强盗进行合并。我们这里写一个merge()函数,将两个强盗合并为一伙。

void merge(int v,int u)
{
    int t1,t2;        //假设t1是v的首领,t2是u的首领,
    t1 = getf(v);       //我们用一个函数getf()来寻找其v的首领t1,u的首领t2,
    t2 = getf(u);
    if(t1 != t2)        
    //如果他们两个人的首领相同,他们自然是一伙人。如果不相同就要合并为一伙。
    {
        f[t2] = t1;      //这里我们规定左为大,即把右边一伙的首领当做左边一伙的小弟,合并为一伙。
    }
    

上面是不是有点迷糊?对getf()不清楚到底是干什么用的。下面就来详细介绍一下:

首先,我们认为10个强盗每个人各自为1伙,一共10伙人,各自为政,所以f[i] = i;他们每个人的领导都是自己。

接下来,假如有人告知1号和2号为1伙,那么这伙人就要有个领导,本例中我们普遍认为号码小的更厉害,让其当做领导。即1号和2号领导都为1。同理3号和4号为1伙,则3号,4号领导都为3,全局变量数组f中的值即为他们的领导值,随着已知信息的不断变化(即告诉你谁谁是一伙),f中的值也不断变化(每个人的领导动态发生变化)。

如果他们的领导相同,那他们自然是一伙人,不用改变,否则就要合并这伙人,把号码小的人当做这伙人的领导。

自然,这里就要自己定义一个函数getf()来寻找每个人的领导。

int getf(int m)
{
    if(f[m]==m)
        return m;            //如果他的领导是自己,自然得到。
    else
    {
        f[m] = getf(f[m]);
        return f[m];        //否则递归寻找自己的领导。
    }
        
}

3.输入条件,由题目得:1和2是一伙,3和4是一伙,……,我们建一个循环输入语句,输入条件,并对其进行合并。

int n,m;
for(int y=0;y<9;y++)
{
    cin>>n>>m;
    merge(n,m);
}
    

4.显然,经过多次处理后,f数组已经分组完成,有几伙强盗,即有几个f[i] 与 i 值相同的个数,即他们最大的领导的个数,设立一个计数变量count,如果f[i]与i相同则count++。

int count = 0;
/*
  ……//相关代码
*/
for(int z = 1;z<=10;z++)
{
    if(f[z] == z)
        count++;
}
cout<

5.得出答案。

 


 

3.完整代码:(c++)

#include 
using namespace std;
int f[11];
int getf(int m)
{
    if(f[m]==m)
        return m;
    else
    {
        //f[m] = getf(f[m]);
        return getf(f[m]);
    }
}
void merge(int v,int u)
{
    int t1,t2;
    t1 = getf(v);
    t2 = getf(u);
    if(t1 != t2)
    {
        f[t2] = t1;
    }
}


int main()
{
    int result = 0;            //为了区分,这里用result代替count。
    for(int i=1; i<=10; i++)
    {
        f[i] = i;
    }
    int n,m;
    for(int j=0; j<9; j++)    //题目告诉9条信息
    {
        cin>>n>>m;
        merge(n,m);
    }
    for(int z=1;z<=10;z++)
    {
        if(f[z]==z)
            result++;
    }
    cout<

 

 


 

4.运行结果

Input

1 2
3 4
5 2
4 6
2 6
8 7
9 7
1 6
2 4

Output

3

Tips

 

数组          f= {  5       1       5       3       5       3       8       9       9       10  }

分别对应   i=  { 1       2       3       4       5       6       7       8       9       10  }

 

5.其他例题:

HDOJ:畅通工程(经典)1232、1558、1811、1829、1198。UESTC:203、1070。


6.致谢

《啊哈算法》这本书详细的介绍了并查集的具体问题并提供实例,借鉴良多。特别感谢!

你可能感兴趣的:(ACM之路)