每日一题之 hiho1066 无间道之并查集

输入
每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第1行为一个整数N,表示黑叔叔总共进行的操作次数。

每组测试数据的第2~N+1行,每行分别描述黑叔叔的一次操作,其中第i+1行为一个整数op_i和两个由大小写字母组成的字符串Name1_i, Name2_i,其中op_i只可能为0或1,当op_i=0时,表示黑叔叔判定Name1_i和Name2_i是同一阵营的,当op_i=1时,表示黑叔叔希望知道Name1_i和Name2_i是否为同一阵营的。

对于100%的数据,满足N<=10^5, 且数据中所有涉及的人物中不存在两个名字相同的人(即姓名唯一的确定了一个人),对于所有的i,满足Name1_i和Name2_i是不同的两个人。

输出
对于每组测试数据,对于黑叔叔每次op_i=1的操作,输出一行,表示查询的结果:如果根据已知信息(即这次操作之前的所有op_i=0的操作),可以判定询问中的两个人是同一阵营的,则输出yes,否则输出no。

样例输入
10
0 Steven David
0 Lcch Dzx
1 Lcch Dzx
1 David Dzx
0 Lcch David
0 Frank Dzx
1 Steven Dzx
1 Frank David
0 Steven Dzx
0 Dzx Frank
样例输出
yes
no
yes
yes

思路:

主要可以复习一下用map来实现对字符串的编号,以及并查集的用法。并查集的思想是每个集合中的元素都指向其集合的一个代表元素,初始化的时候将每个集合 f [ i ] 看作只由元素 i 组成的集合,并且集合 f [ i ] 的代表元素就是 i f [ i ] 表示的是i元素所在集合的代表元素)(即初始化 f [ i ] = i )并查集还有一个合并的操作,并查集关于合并的思想是:首先看元素A,B的代表元素(即 f [ A ] f [ B ] ) 是否相同,相同的话自然不需要合并,因为集合 f [ A ] f [ B ] 的相等话,就证明这两个数属于同一个集合了。那么不等于的时候进行合并的时候是 先找到 A 的代表元素 fa 即 f a = f [ A ] 以及 B 的代表元素 fb f b = f [ B ] 如果令 f [ f a ] = f b 的话,就相当于进行了合并,因为 fa 是 集合 A 所在集合的代表元素,现在 fa 指向了 fb 也就是说 元素A所在集合的代表元素 fa 的代表元素是 fb。这就意味着如果 A 所在集合的其他元素的一直往上走的话会找到 fb。于是这两个集合最终有共同的代表元素fb,相当于进行了合并了。

#include <iostream>
#include <cstring>
#include <map>

using namespace std;

const int maxn = 1e5+5;

int f[maxn];

int find(int x)
{
    if (f[x] == x) return x;
    else
        return f[x] = find(f[x]);
}

int main()
{
    int n,op;
    string n1,n2;
    cin >> n;
    map<string,int>mp;
    while(n--){
        cin >> op >> n1 >> n2;
        int &x = mp[n1];
        if (x == 0)
            f[x = mp.size()] = x; // 这里是先改变x的值再赋值
        int &y = mp[n2];
        if (y == 0)
            f[y = mp.size()] = y;

        int fx = find(x);
        int fy = find(y);
        if (op) {
            if (fx != fy) cout << "no" << endl;
            else
                cout << "yes" << endl;
        }
        else {
            if (fx != fy)
                f[fx] = fy;
        }
    }


    return 0;
}

猜你喜欢

转载自blog.csdn.net/u014046022/article/details/80719142