• 【进阶——种类并查集】hdu 1829 A Bug's Life (基础种类并查集)TUD Programming Contest 2005, Darmstadt, Germany


    先说说种类并查集吧。

    种类并查集是并查集的一种。但是,种类并查集中的数据是分若干类的。具体属于哪一类,有多少类,都要视具体情况而定。当然属于哪一类,要再开一个数组来储存。所以,种类并查集一般有两个数组,一个存并查集内的父子关系,一个存各个节点所属的种类关系。

    以这道题为例(题意在后面,如果没有读题,可以先看完题在来看这部分)——

    这道题很明显,将bug分成两类,一公一母。但是实际上我们并不关心它是公的还是母的,只关心它们之间是同性还是异性。所以,我们可以设与并查集的根节点同性的为0,反之为1。所以,我们就需要在int mfind(int x)里加上一个对于种类进行的操作。这个操作需要不停地修改每个bug所属的种类。

    需要反复修改的理由:每次合并,我们都会将两个集合合并成一个集合,此时就会有一个根节点变成普通节点。那么之前在这个根节点之后的节点所拥有的关系就需要修改。前面已经说了,我们设置的01关系是和根节点相关的关系,那么当根节点变化的时候,我们的对应关系就需要变化。

    值得注意的是,这个修改并不需要立刻进行,对于消失的那个根节点上的子节点,只需要在下次查询的时候进行就可以(类似于离线操作。有些人的代码在进行合并之后就进行了一次查找,目的就是马上将查找的节点的种族及时修改,个人认为这是没有必要的)。但是对于消失的那个根节点,我们则需要及时将它与仍然存在的根节点的关系及时修改,因为这里是首次合并时进行的种类关系确定,即,将单个节点合并成一个集合时进行的种类关系确定。

    种类关系的确定所使用的运算十分巧妙,我只是大概明白,却还不是完全清楚。种族关系确定时,如果两者的种族相同,那么需要修改其中一方的种族,如果两者的种族不同,那么保持不变。当然,这是这道题的操作,不同的题目会有不同的要求。

    题意——

    有两种bug,一种是公的,一种是母的。

    现在给他们配对,只能公配母,否则就会出bug了,问你他所提出的配对方式有没有bug。

    输入——

    第一行一个整数t,表示有t组数据。

    接下来,每组数据第一行有两个整数n, m,表示共有n个虫子,m种配对。

    接下来m行,每行两个整数a, b,表示一种配对。

    输出——

    输出是第几组数据

    输出是否存在bug。

    数据举例——

    1

    3 3

    1 2

    1 3

    2 3

    这里表示共有1组数据,这组数据包含3只bug和3种配对。

    由于1可以与2配,1可以与3配,那么2, 3同性……所以这一组是个bug(这里不存在gay和百合……)

    上代码——

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <cmath>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 const int M = 2010;
     8 
     9 int fm[M];
    10 int n, m, t;
    11 bool tp[M], flag;   //tp[]记录种族,flag记录是否存在bug
    12 
    13 int mfind(int x)                //查询操作
    14 {
    15     if(fm[x] == x) return x;
    16     int fx = fm[x];
    17     fm[x] = mfind(fm[x]);
    18     tp[x] = tp[fx]^tp[x];       //修改种族,对查询的子节点进行
    19     return fm[x];
    20 }
    21 
    22 void mmerge(int x, int y)
    23 {
    24     int fx = mfind(x);
    25     int fy = mfind(y);
    26     //printf("%5d%5d
    ", fx, fy);
    27     if(fx != fy)                    //合并操作
    28     {
    29         fm[fy] = fx;
    30         tp[fy] = tp[x]^tp[y]^1;     //修改种族,对消失的根节点进行
    31     }
    32     else if(tp[x] == tp[y]) flag = 1;   //如果两个节点属于同一并查集且种族相同,则出现bug
    33 }
    34 
    35 void init()         //各种数据初始化
    36 {
    37     memset(tp, 0, sizeof(tp));      //种类初始化,所有节点都和自己属于同一种类
    38     flag = 0;                       //目前没有bug
    39     scanf("%d%d", &n, &m);
    40     for(int i = 1; i <= n; i++) fm[i] = i;  //并查集初始化,这个不用多说了吧
    41 }
    42 
    43 void work()
    44 {
    45     for(int i = 0; i < m; i++)
    46     {
    47         int a, b;
    48         scanf("%d%d", &a, &b);
    49         if(!flag) mmerge(a, b);     //不存在bug才进行并查集操作,
    50     }
    51 }
    52 
    53 void output(int tm)
    54 {
    55     printf("Scenario #%d:
    ", tm);
    56     if(!flag) printf("No suspicious bugs found!
    ");
    57     else printf("Suspicious bugs found!
    ");
    58     //for(int i = 1; i <= n; i++) printf("%5d", tp[i]);
    59     //printf("
    ");
    60     printf("
    ");
    61 }
    62 
    63 int main()
    64 {
    65     //freopen("test.in", "r", stdin);
    66     scanf("%d", &t);
    67     for(int tm = 1; tm <= t; tm++)
    68     {
    69         init();
    70         work();
    71         output(tm);
    72     }
    73     return 0;
    74 }
    View Code

     当然,这个代码里我使用的是抑或^符号来处理的,主要原因是这里一共只有两个种类(使用bool数组进行记录也是如此的原因),如果种类比较多,就需要使用%符号或者其他的方式处理了。

    ps:我也是一边写博客一边思考的,在写完的时候对于这个算法的思路确实又清晰了不少,看来写博客还是很有用处的。

  • 相关阅读:
    Security » Authorization » 要求处理器中的依赖注入
    Security » Authorization » 基于自定义策略的授权
    Security » Authorization » 基于声明的授权
    Security » Authorization » 基于角色的授权
    CentOS下下载软件,不安装的方法
    rsync+sersync+inotify实现服务器间文件同步之一
    linux查看机器负载
    htpasswd建立和更新存储用户名、密码
    由异常掉电问题---谈xfs文件系统
    Flashcache基本使用及注意事项
  • 原文地址:https://www.cnblogs.com/mypride/p/4743357.html
Copyright © 2020-2023  润新知