排座位(并查集)

48 阅读2分钟

L2-010 排座位 (pintia.cn)

原题题面

image.png

题目描述

宴会需要进行排座位,针对同席的一对客人的不同关系给出相应回复。

注意朋友的朋友与自己具有朋友关系。

输入样例

7 8 4
5 6 1
2 7 -1
1 3 1
3 4 1
6 7 -1
1 2 1
1 4 1
2 3 -1
3 4
5 7
2 3
7 2

输出样例

No problem
OK
OK but...
No way

题目分析

本题为 并查集 的一个应用。

首先介绍一下并查集的核心代码:

int find(int x)
{
    if (x != p[x]) p[x] = find(p[x]);
    return p[x];
}

p[x] 意为节点 x 所在树的根节点,算法首先初始化所有节点的初始节点为自己,每次需要合并两棵树的时候都是在将其中一棵树所有节点的 p[x] 更改为另一棵树的根节点。

find() 函数采取了一种递归的思想,对根节点不是自己的节点,我们需要依次向深层递归,最终找到根节点,再依次回溯,将递归路径上所有节点的根节点都重置为根节点。

当判断 x,y 的关系时,需要先分别进行一次 find() 再判断 p[x] 是否等于 p[y] 即可。

回到本题,由于存在朋友的朋友也是朋友的限制,很自然的我们便可以想到用并查集将所有有朋友关系的人都挂在一棵树上,即都拥有相同的根节点。

而对于直接的敌对关系,我们可以新开一个二维数组来记录。

最终对题目所给的四种关系进行讨论即可。

Accept代码

#include <bits/stdc++.h>

using namespace std;

const int N = 110;

int n, m, k;
int p[N];
int g[N][N];

int find(int x)
{
    if (x != p[x]) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m >> k;
    for (int i = 1; i <= n; i ++) p[i] = i;
    for (int i = 0; i < m; i ++)
    {
        int a, b, op;
        cin >> a >> b >> op;
        if (op == 1) p[find(a)] = p[find(b)];
        else g[a][b] = g[b][a] = 1;
    }
    for (int i = 0; i < k; i ++)
    {
        int a, b;
        cin >> a >> b;
        if (find(a) == find(b))
        {
            if (g[a][b]) cout << "OK but..." << "\n";
            else cout << "No problem" << "\n";
        }
        else
        {
            if (g[a][b]) cout << "No way" << "\n";
            else cout << "OK" << "\n";
        }
    }
    return 0;
}