杭电3635-Dragon Balls-并查集之路径压缩

龙珠追踪:并查集与路径压缩
本文介绍了一道编程题目,该题目通过使用并查集数据结构及路径压缩技术来解决龙珠位置追踪的问题。具体实现包括城市间的龙珠转移以及获取指定龙珠的位置、所在城市龙珠总数和移动次数。
Problem Description
Five hundred years later, the number of dragon balls will increase unexpectedly, so it's too difficult for Monkey King(WuKong) to gather all of the dragon balls together.

His country has N cities and there are exactly N dragon balls in the world. At first, for the ith dragon ball, the sacred dragon will puts it in the ith city. Through long years, some cities' dragon ball(s) would be transported to other cities. To save physical strength WuKong plans to take Flying Nimbus Cloud, a magical flying cloud to gather dragon balls.
Every time WuKong will collect the information of one dragon ball, he will ask you the information of that ball. You must tell him which city the ball is located and how many dragon balls are there in that city, you also need to tell him how many times the ball has been transported so far.
 

Input
The first line of the input is a single positive integer T(0 < T <= 100).
For each case, the first line contains two integers: N and Q (2 < N <= 10000 , 2 < Q <= 10000).
Each of the following Q lines contains either a fact or a question as the follow format:
  T A B : All the dragon balls which are in the same city with A have been transported to the city the Bth ball in. You can assume that the two cities are different.
  Q A : WuKong want to know X (the id of the city Ath ball is in), Y (the count of balls in Xth city) and Z (the tranporting times of the Ath ball). (1 <= A, B <= N)
 

Output
For each test case, output the test case number formated as sample output. Then for each query, output a line with three integers X Y Z saparated by a blank space.
 

Sample Input
  
2 3 3 T 1 2 T 3 2 Q 2 3 4 T 1 2 Q 1 T 1 3 Q 1
 

Sample Output
  
Case 1: 2 3 0 Case 2: 2 2 1 3 3 2


传送门

解题思路:

该题大意是有n个龙珠在n个城市,现分别执行以下两种操作:“T”表示将x龙珠移到y城市,而“Q”表示输出x所在城市,该城市的龙珠总数,以及该龙珠移动次数。

此题就是一道纯并查集的简单应用,但却涉及到了路径压缩的知识。下面先来介绍一下路径压缩。

关于路径压缩,维基百科解释如下:

“路径压缩”,是一种在执行“查找”时扁平化树结构的方法。关键在于在路径上的每个节点都可以直接连接到根上;他们都有同样
的表示方法。为了达到这样的效果,Find递归地经过树,改变每一个节点的引用到根节点。得到的树将更加扁平,为以后直接或者
间接引用节点的操作加速
那什么是扁平化呢?


其实就是减少了树的深度,使得下一次查找更加方便。

下面是迭代版路径压缩:

int findRoot(int x)
{
            int tempRoot;
            int root = x;
            while(root != parent[root])
                        root = parent[root];

            while(x != root)
            {
                        tempRoot = parent[x];
                        parent[x] = root;
                        x = tempRoot;
            }
            return root;
}

但有时候有些题却不能用迭代版,于是有下面的递归版:
    int find_root(int x)  
    {  
        int root;      
        root = x;      
        while( root != father[root])              
            root = father[root];          
        return root;           
    }  

再回到问题上来,前面2个很好实现,最后移动次数有点麻烦,将要转移的龙珠所在城市的根节点的转移次数加1,等到路径压缩时,子节点自己移动次数加上根节点移动次数,就是节点总共移动次数。

可能有点绕,稍微从文字角度上说一下。

比如说要把城市i的龙珠转移到j,先让i所在树的根(这里的根其实就是i城市本来有的龙珠也就是i号龙珠)的转移次数为1(因为第一次转移)
然后从开始向根节点路径压缩。假设从i到顶点一共有四层(也就是说,目前i城市里边有4颗龙珠,根节点龙珠是i号)
根结点为1号龙珠(根节点),然后第二层2号龙珠,第三层3号龙珠,第四层4号龙珠.那么,第一层路径压缩过程中进入第二层,然后第三层,然后第四层,第四层也就是根,找到根结点,返回第三层,第三层+上第四层的转移次数,然后返回第二层,第二层转移次数加上第三层转移次数,返回第一层,第一层转移次数加上第二层转移次数,同时,在这个过程中也把下面3层的结点的父节点直接更新为和根结点,也就是1相连。
下附代码:
#include <iostream>
#include <cstdio>

using namespace std;

const int maxn = 10010;

int city[maxn];//表示i号城市有几个球
int parent[maxn];//表示i球在哪个城市
int transport[maxn];//表示i球被转移了几次
int t,n,q;

void makeset()
{
      for(int i=0;i<=n;i++)
      {
            city[i] = 1;
            parent[i] = i;
            transport[i] = 0;
      }
}

int findRoot(int x)
{
      if(x == parent[x])
            return x;
      int tempRoot = parent[x];
      parent[x] = findRoot(parent[x]);//路径压缩
      transport[x] += transport[tempRoot];
      return parent[x];
}

void Union(int x,int y)
{
      int xRoot = findRoot(x);
      int yRoot = findRoot(y);
      if(xRoot != yRoot)
      {
            parent[xRoot] = yRoot;
            transport[xRoot] = 1;
            city[yRoot] += city[xRoot];
            city[xRoot] = 0;
      }
}

int main()
{
      scanf("%d",&t);
      int num = 1;
      while(t--)
      {
            scanf("%d %d",&n,&q);
            printf("Case %d:\n",num++);
            makeset();
            char str[10];
            while(q--)
            {
                  scanf("%s",str);
                  if(str[0] == 'T')
                  {
                        int x,y;
                        scanf("%d %d",&x,&y);
                        Union(x,y);
                  }
                  else if(str[0] == 'Q')
                  {
                        int x;
                        scanf("%d",&x);
                        int k =findRoot(x);
                        printf("%d %d %d\n",k,city[k],transport[x]);
                  }
            }
      }
      return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值