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

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

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

通过一道题目实例说明:

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;

}

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值