[AcWing 258] 石头剪刀布

image
image
image

带扩展域的并查集


点击查看代码
#include<bits/stdc++.h>

using namespace std;

typedef long long LL;

const int N = 1e6 + 10;

int n, m;
int p[N];

struct Node
{
    int a, b;
    char op;
} s[N];

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

void merge(int a, int b)
{
    int pa = find(a), pb = find(b);
    p[pa] = pb;
}

bool same(int a, int b)
{
    return find(a) == find(b);
}

bool check(int a, int b, char c)
{
    if (c == '=') {
        if (same(a, b + n) || same(a + n, b))
            return false;
        merge(a, b);
        merge(a + n, b + n);
        merge(a + n + n, b + n + n);
    }
    else if (c == '>') {
        if (same(a, b) || same(a + n, b))
            return false;
        merge(a, b + n);
        merge(a + n, b + n + n);
        merge(a + n + n, b);
    }
    else {
        if (same(a, b) || same(a, b + n))
            return false;
        merge(a + n, b);
        merge(a + n + n, b + n);
        merge(a, b + n + n);
    }
    return true;
}

void solve()
{
    while (cin >> n >> m) {
        for (int i = 1; i <= m; i ++) {
            int a, b;
            char op;
            cin >> a >> op >> b;
            s[i] = {a, b, op};
        }
        // 记录裁判的个数
        int cnt = 0;
        // 记录裁判的编号
        int res = 0;
        // 记录得到裁判信息的行号
        int the_line = 0;
        // 假设编号为 t 的人是裁判
        for (int t = 0; t < n; t ++) {
            // 每次都要将集合初始化
            for (int i = 0; i < 3 * n; i ++)
                p[i] = i;
            bool is_ok = true;
            for (int i = 1; i <= m; i ++) {
                auto& [a, b, op] = s[i];
                // 将裁判跳过
                if (a == t || b == t)
                    continue;
                // 检查是否合法,合法则合并集合,不会进入if中
                if (!check(a, b, op)) {
                    // 不合法才会进入
                    is_ok = false;
                    // 其他人不是裁判的最大轮数
                    if (the_line <= i)
                        the_line = i;
                    break;
                }
            }
            if (is_ok) {
                res = t;
                cnt ++;
            }
        }
        if (!cnt)
            printf("Impossible\n");
        else if (cnt > 1)
            printf("Can not determine\n");
        else
            printf("Player %d can be determined to be the judge after %d lines\n", res, the_line);
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    solve();

    return 0;
}

  1. 判断裁判的思路
    先假设一定存在裁判,枚举把每个人当作裁判,然后将包含他的数据跳过,判断其他数据是否会推出矛盾,如果没有矛盾,则说明这个人可能是裁判,就把 \(cnt + 1\),考虑以下几种情况:
    ① 如果只有一位裁判,那么只有当枚举到把他当作裁判时,其他数据才会没有矛盾,\(cnt + 1\) 只会执行一次,最终 \(cnt = 1\)
    ② 如果有裁判,但是不能确定哪个是裁判,那么有多个人选,把他当作裁判时,其他数据没有矛盾,\(cnt + 1\) 会执行超过一次,\(cnt > 1\)
    ③ 其他情况是上述两种情况的补集,也就是必须没有裁判或者必须有多个裁判,\(cnt\) 只能取 \(0\)(没有裁判这种情况首先就不符合前提假设 “先假设一定存在裁判”,\(cnt = 0\) 只能说明是 “必须没有裁判或者必须有多个裁判” 两种情况的一种,不能具体到是哪一种情况)
  2. 扩展域
    对于 \(a\) 号小朋友,\(a\) 代表该小朋友出的是石头,\(a + n\) 代表该小朋友出的是剪刀,\(a + n + n\) 代表该小朋友出的是布
  3. 最早的找到裁判的时间 \(\Leftrightarrow\) 判断出除他以外的人都不是裁判的时间 \(\Leftrightarrow\) 判断出其他人不是裁判的时间的最大值
posted @ 2022-08-16 21:18  wKingYu  阅读(17)  评论(0编辑  收藏  举报