码迷,mamicode.com
首页 > 其他好文 > 详细

并查集

时间:2020-03-20 00:47:59      阅读:55      评论:0      收藏:0      [点我收藏+]

标签:超时   form   can   lin   which   空格   并查集   lib   没有   

 

转自:https://www.cnblogs.com/yinbiao/p/9464390.html

题目: http://www.fjutacm.com/Contest.jsp?cid=870#P3

这题真的搞得我头痛,可能是我对并查集还没有深刻的理解吧

 

 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.

他的国家有N个城市,世界上正好有N个龙珠。首先,为了第i个龙珠,神圣的龙会把它放在第i个城市。经过漫长的岁月,一些城市的龙珠会被运送到其他城市。为了节省体力,悟空计划乘飞天云,一种神奇的飞天云来收集龙珠。

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(0 < T <= 100)。

对于每种情况,第一行包含两个整数:N和Q (2 < N <= 10000, 2 < Q <= 10000)。

下列Q行中的每一行都包含一个事实或一个问题,其格式如下: 


  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.
T A B 表示:所有和A在同一个城市的所有龙珠都被运到了B球所在的城市。你可以假设这两个城市是不同的  (PS:注意是所有)
  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)
Q A 表示:悟空想知道X(第 A 个球所在城市的 id ), Y( 第X个城中球的数量 ),Z( 第 A 个球被运送的次数 )。  (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.
对于每个测试用例,输出作为示例输出的测试用例编号。然后,对于每个查询,输出一行三个由空格分隔的整数X Y Z。
 
2
3 3
T 1 2
T 3 2
Q 2
3 4
T 1 2
Q 1
T 1 3
Q 1
 
Case 1:
2 3 0
Case 2:
2 2 1
3 3 2
 
 
我在网上看了好几篇博客,好像都觉的这题挺简单的,都没有什么解释。可惜这题我卡在移动次数卡了好久,那个球的移动次数真的有点不理解,怪我太笨   ((ノ*T_T*)
 
一,输出的问题,注意理解,测试用例编号是在事实和询问之前就打印出来了
 
二,询问的第二个,之前做过,不想多说,就是求某个集合的元素数量,直接在构造一个数组,在链接两个点的时候同时递加,最后保存在根节点就好了
 
三,询问的第一个,说之前先讲一下,T A B,这是一个集合的整体移动的,这也是这题为什么用并查集的原因,一整个集合指向一个点(要移动的目的城市),这不就是并查集压缩路径之后的样子吗?
我们只要在连接两个点的时候,注意让目的城市成为父节点,就可以构造出要移动的集合了。 所以,最后,根节点 是有特殊含义的,其实也就是   find (x) = 第 x个球所在城市的 id,也就是第一个询问了
 
四,询问的第三个
说之前,我想先讲一下,find 函数
int find(int x)   // 可将递归分为 两个过程
{
    int a = x;
    while (p[x] != x)   // 搜索的过程
        x = p[x];
    while (a != x)     // 回溯的过程
    {
        int t = p[a];
        p[a] = x;
        a = t;
    }
    return x;
}
int Find(int x)
{
    if (p[x] != x)
        p[x] = Find(p[x]);
    return p[x];
}

find 函数可以要实现两个功能  ① 找到根结点  ② 压缩路径

函数实现可以有两种方法  ① 循环  ② 递归

就是在做这个时,我第一次想到,可以将递归分为两部分  ① 调用自己之前的代码  ② 调用自己之后的代码

愚以为,第一部分 是 搜索 实现找到 根结点 的功能 ;第二部分是 回溯 实现 压缩路径 的功能

 

想法一,之前打算构造一个 mov 数组,在 join 函数连接两个点的时候,让移动的那个城市 id ,mov[id]++,

后发现 当别的点移动时,是会带动集合的点也跟着移动的,这样子,其他点就没有记录到

想法二,还是在 join 函数,连接两个点之前,构造一个函数,让所有指向这个点的 点的mov++, 结果超时了

超时代码:

void fun(int x)
{
    for (int i = 0; i <= n; i++)
    {
        if (p[i] == x)
            mov[i]++;
    }
}
void join(int x, int y)
{
    x = find(x), y = find(y);
    if (x == y)
        return;
    fun(x);    // 
     p[x] = y;
     num[y] += num[x];
}

 

想法三,在想法一的基础上,加一个函数,在查询时,直接让 x 的所有父节点的mov[] 加起来,结果 wa, 原因:忘记被压缩路径了,找不回去了

错误代码

int move(int x)
{
    int sum = 0, a = x;
    while (p[x] != x)
    {
        sum += mov[x];
        x = p[x];
    }
    sum += mov[x];
    return sum;
}
void join(int x, int y)
{
    x = find(x), y = find(y);
    if (x == y)
        return;
     p[x] = y;
     num[y] += num[x];
     mov[x]++;
}
if (str[0] == Q)
{
    scanf("%d", &x);
    int k = find(x);
    printf("%d %d %d\n", k, num[k], mov[x]);
}

 

所以,我就直接百度了,?乛?乛? (●´∀`●)

是基于我对想法一,利用 find 的回溯对其他点进行处理

说实话,由于没解释我这个代码直接看了一天,才理解,现在已然是在熬夜了

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#define N 10005
int p[N], num[N], mov[N];
int n, m;
int find(int x)
{
    if (p[x] != x)
    {
        int t = p[x];
        p[x] = find(p[x]);
        mov[x] += mov[t];
    }
    return p[x];
}
void join(int x, int y)
{
    x = find(x), y = find(y);
    if (x == y)
        return;
     p[x] = y;
     num[y] += num[x];
     mov[x]++;
}
int main(void)
{
    int t; scanf("%d", &t);
    int ci = 0;
    while (t--)
    {
        scanf("%d%d", &n, &m);
        for (int i = 0; i <= n; i++)
        {
            p[i] = i;
            num[i] = 1;
            mov[i] = 0;
        }

        printf("Case %d:\n", ++ci);
        char str[10];
        while (m--)
        {
            scanf("%s", str);
            int x, y;
            if (str[0] == T)
            {
                scanf("%d%d", &x, &y);
                join(x, y);
            }
            if (str[0] == Q)
            {
                scanf("%d", &x);
                int k = find(x);
                printf("%d %d %d\n", k, num[k], mov[k]);
            }
        }
    }

    system("pause");
    return 0;
}

 

 

 
 

========== ======= ======= ====== ====== ===== ==== === == =

尘曲   七堇年

凡心所向,素履所往; 生如逆旅,一苇以航。

稣合于言,安之若素; 自言自语,无喜无悲。

凡心所向,素履所往; 生如逆旅,一苇以航。

三月桃花,四月欢唱; 两人一马,明日故乡。

流浪陌路,暖然绯凉; 写意人生,相识一场。

不关此世,不负己心; 我自倾杯,且君随意。

 

并查集

标签:超时   form   can   lin   which   空格   并查集   lib   没有   

原文地址:https://www.cnblogs.com/asdfknjhu/p/12528457.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!