POJ-1703-Find them, Catch them 解题报告

       一道标准的关系型并查集题。普通的并查集是给几个同类的元素,而关系型并查集是给不同类的元素,然后求各个元素之间的关系。


       题目大意是:在一个城市里有两种不同的犯罪团伙。首先输入T表示有T组测试,然后输入N和M,表示有N个罪犯(编号从1到N)而且接下来有M个操作。操作分为两种:

1.D a b,表示编号为a和b的两个罪犯属于不同的犯罪团伙;

2.A a b,表示询问编号为a和b的两个罪犯是否是同一个犯罪团伙或者不确定。

对于每一个A操作,根据题意都要有相应的回答(输出)。


       接下来是解题思路:既然是用并查集来实现,我们可以定义一个整型数组表示当前节点与父节点的关系,偶数代表同类,奇数代表异类。初始化时每一个节点的父节点都为自己,关系数都为0。

每次进行查找一个节点的根节点时,都应该进行路径压缩,将沿途节点的父节点都修改为根节点,而关系数也应该对应地修改为与根节点的关系。该节点与根节点的关系数为该节点到根节点沿途所有节点的关系数之和(不包括该节点与当前父节点的关系数)。

进行合并操作时我们知道需要合并的两个原节点的关系为异类,如果这两个原节点的根节点相同(也就是说在同一个集合里面),那么我们可以直接忽略合并操作;如果这两个原节点的根节点不相同,那么应该对这两个原节点的根节点进行合并,合并后应该满足这两个原节点的关系为异类,我们需要并且只需要更新拥有新根的原根节点信息即可
。合并方式为将其中一个根节点的父节点修改为另一个根节点,而该根节点的关系数则应修改为两个原节点与原根节点的关系数之和加1,因为已知的信息是两个原节点的关系为异类,这样的操作是根据两个原节点之间关系以及两个原节点与对应根节点之间的关系得来的。


       下面是解题代码

 1 #include <stdio.h>
 2 #define N 100001
 3 
 4 int bleg[N];    //存储父节点
 5 int rela[N];    //存储与父节点的关系,偶数为同类,奇数异类
 6 int t;
 7 int n, m;
 8 char *ns = "Not sure yet.";
 9 char *dg = "In different gangs.";
10 char *sg = "In the same gang.";
11 
12 void Init();        //初始化
13 
14 int Find(int x);    //并查集查找
15 
16 void Union(int x, int y);   //并查集合并
17 
18 int main()
19 {
20     char a;
21     int x, y;
22     scanf("%d", &t);
23     while (t--)
24     {
25         scanf("%d %d", &n, &m);
26         Init();           
27         while (m--)
28         {
29             scanf(" %c %d %d", &a, &x, &y);
30             if (a == 'D')
31             {
32                 Union(x, y);
33             }
34             else
35             {
36                 if (Find(x) != Find(y))     //如果两节点的根节点不相同,则无法知道两节点的关系
37                 {
38                     puts(ns);
39                 }
40                 else if ((rela[x] + rela[y]) % 2 == 0)  //推论可得,表示两节点的关系数为两节点与共同的根结点的关系数之和
41                 {
42                     puts(sg);
43                 }
44                 else
45                 {
46                     puts(dg);
47                 }
48             }
49         }
50     }
51     return 0;
52 }
53 
54 void Init()     //初始化
55 {
56     int i;
57     for (i=0; i<N; i++)
58     {
59         bleg[i] = i;
60         rela[i] = 0;
61     }
62     return;
63 }
64 
65 int Find(int x)         //并查集查找
66 {
67     int y = bleg[x];
68     int sum = rela[x];
69     int temp, z;
70     while (y != bleg[y])    //当y不是根节点时
71     {
72         sum += rela[y];
73         y = bleg[y];
74     }
75     while (x != bleg[x])    //路径压缩并将沿途所有节点的关系改成与根节点的关系
76     {
77         z = bleg[x];
78         temp = rela[x];
79         rela[x] = sum;
80         bleg[x] = y;
81         sum -= temp;
82         x = z;
83     }
84     return y;
85 }
86 
87 void Union(int x, int y)    //并查集合并
88 {
89     int fx = Find(x);
90     int fy = Find(y);
91     if (fx == fy) return;
92     bleg[fx] = fy;
93     rela[fx] = rela[x] + rela[y] + 1;
94     return;
95 }

转载于:https://www.cnblogs.com/JZQT/p/3802448.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值