C++并查集——通过一道实例说明

来源:互联网 发布:深圳正规软件培训 编辑:程序博客网 时间:2024/06/06 03:04

并查集在对待某一类问题时十分有效。

    能够方便解决联通性的问题。

通过一道题目实例说明:

Y到岛上淘金,湖中共有N个小岛,每个小岛上都有若干个金币

岛与岛之间有桥连接,一共有M座桥,小Y不会游泳,所以他只能通过桥访问其他的小岛

但是小Y为了淘金,找万能的魔法师小L要来了两颗传送卷轴,传送卷轴可以让他移动到另外一个小岛(无论是否之间是否有桥相连)

1号岛是大陆,小Y起初站在1号岛上,小Y访问完小岛之后会回到1号岛,毕竟他不想流落荒岛变成野人

Y最多可以获得多少个金币

输入

n m

W1 W2 ... Wn

A1 B1

A2 B2

...

Am Bm

上面W1Wn表示每座岛有多少个金币

接下来m对数字

每对数字表示哪两个岛之间有一座桥

注意桥是双向的,并且可能两座岛之间会有多座桥

桥可以无限次重复走

 

输出

最多的金币数目

样例输入

5 4

1 1 3 4 10

1 2

2 1

3 4

4 3

样例输出

12

提示

 

直观解决的思路非常简单:

连起来的岛都归到一块

1号岛相连的岛的金币加上 除此外连接起来的岛的金币 之和即为最终答案


10为传送到达

 

问题的核心在于如何快速的将这些岛分到一块。

即点的联通问题。

 

来看具体实现

//pre用于存其上一个连接的地方,pre[i] == i则代表为根节点

//w与此题相关

int pre[1000],w[1000],root[1000]={0};

 

//查找函数,查找它的根结点

intfind_root(int x){

   int r = x;

   //查找根节点

   while(r != pre[r])

       r = pre[r];

   

//以下是并查集的路径缩短,能够节约时间以及防止一些极端情况如一串相连

int t;

   while(x != pre[x]){

       t = pre[r];

       pre[x] = r;

       x = t;

   }

   return r;

}

//连接操作

void join(intx,int y){

   int fx = find_root(x),fy = find_root(y);

   //判别根节点是否一样,即大家本来在不在一块

   //不在则使其中一个根结点连接在另一个上

if(fx!=fy){

       pre[fy] = fx;

   }

}

 

int main()

{

   scanf("%d%d",&N,&M);

   //initialize the pre

//初始化pre数组,一开始都没有相连,所以为其自身

//0或者1依据情况使用

   for(int i = 1;i<=N;i++){

       pre[i] = i;

       scanf("%d",&w[i]);

   }

  //join

   while(M){

       int x,y;

      //输入连接

       scanf("%d%d",&x,&y);

       join(x,y);

       M--;

   }

  

   int max_n = 0;

   //将所有多并行连接改到一个根结点连接

   //因为在使用join查找时有可能会出现上面一张所示并非单层相连的场景。

for(int i = 1;i<=N;++i)

       find_root(i);

//可以通过判别pre[i]是否等于i判别该点是否为根结点

//最终在一块的都会加到同一方位root[]  

//后面与该题相关

 for(int i = 1;i<=N;i++){

       root[pre[i]] +=w[i];

   }

   for(int i = 1;i<=N;i++){

       if(i == pre[1]) continue;

       if(root[i] > max_n) max_n = root[i];

   }

   cout<<(root[pre[1]]+max_n)<<endl;

}