【带权并查集+枚举】Rochambeau POJ - 2912

Rochambeau POJ - 2912

题意:

\(N\)个小孩玩\(M\)轮剪刀石头布。其中\(1\)个小孩是法官,剩下的小孩分为\(3\)组(可能有空的组)。每轮游戏随机从所有小孩中抽两个人,同组的小孩会出同样的手势,不同组的小孩会出不同的手势,法官可以随意出手势。现给出\(M\)轮游戏的结果,问哪个小孩是法官,以及猜出法官所需要的最少结果数。

思路:

剪刀石头布之间的输赢关系和食物链那题是一样的,所以公式可以照搬。

但与做过的带权并查集的题目不同的是,本题不仅要找到矛盾,还得找到谁是矛盾的源头。

这里用枚举法,假设第\(i\)个小孩是法官,再看剩下的与\(i\)无关的关系是否矛盾。因为除了法官之外其它小孩都不能随意出拳,所以与\(i\)无关的输赢结果都是可推理的。如果不矛盾,则\(i\)可以是法官;如果有矛盾,\(i\)一定不是法官。

  • Impossible:所有小孩中没有一个可能是法官

  • Can not determine:所有小孩中有多于一个可能是法官

  • Player \(i\) can be determined to be the judge after \(x\) lines:所有小孩中只有一个可能是法官

在枚举的过程中统计“可以是法官”的人数即可判断出是三种情况中的哪一种。

至于最少多少行可以确定法官,应当逆向思考:最少多少行可以确定剩下\(n-1\)个人不是法官。那么可以在枚举的过程中,找出判断某个小孩不是法官所需的最多的行数\(cnt\)。因为其它小孩都已经在\(cnt\)行之前就可以确定不是法官了。到第\(cnt\)行的时候,这\(n-1\)个小孩都可以确定不是法官。

因为没意识到我的数组需要存m条关系,把maxn设成了510,WA到怀疑人生……

期间找了若干题解来看,发现思路都一致,但我的代码还更简洁一些。

const int maxn = 2000 + 100;

int fa[maxn], rela[maxn];
int x[maxn], y[maxn], r[maxn];

int find(int x) {
    if (fa[x] == -1) return x;
    int tmp = find(fa[x]);
    //0与父节点平局,1赢了父节点,2输了
    rela[x] = (rela[x] + rela[fa[x]]) % 3;
    return fa[x] = tmp;
}

bool merge(int x, int y, int rr) {
    int r1 = find(x);
    int r2 = find(y);
    if (r1 == r2) {
        return rr == (rela[x] - rela[y] + 3) % 3;
    }
    else {
        fa[r1] = r2;
        rela[r1] = (rela[y] - rela[x] + rr + 3) % 3;
        return true;
    }
}

int main()
{
    //ios::sync_with_stdio(false);
    int n, m;
    while (cin >> n >> m) {
        int judge = 0;
        int cnt = 0;
        int ans = 0;
        for (int i = 1; i <= m; i++) {
            char s;
            cin >> x[i] >> s >> y[i];
            if (s == '<') r[i] = 2;
            else if (s == '>') r[i] = 1;
            else r[i] = 0;
        }
        for (int i = 0; i < n; i++) {
            //先假设小孩i是judge,再以此出发判断与他无关的其它所有关系是否存在矛盾
            int ok = 1;
            for (int i = 0; i <= n; i++) fa[i] = -1;
            memset(rela, 0, sizeof(rela));
            //注意要在这里初始化
            for (int j = 1; j <= m; j++) {
                int u = x[j], v = y[j], rr = r[j];
                if (u == i || v == i) continue;
                //不用判断和judge相关的关系
                if (!merge(u, v, rr)) {
                    ok = 0;
                    //其它关系出现矛盾,则小孩i不可能是judge
                    cnt = max(j, cnt);
                    //记录一下最多需要多少条关系来排除某个小孩的嫌疑
                    break;
                }
            }
            if (ok) {
                ++judge;
                //统计“可能是法官”的人数
                ans = i;
                //记录法官的编号
                //需要输出ans的时候必然只找到一个法官
            }
        }
        if (judge == 0) cout << "Impossible" << endl;
        else if (judge > 1) cout << "Can not determine" << endl;
        else cout << "Player " << ans << " can be determined to be the judge after " << cnt << " lines" << endl;
    }
    return 0;
}
posted @ 2020-08-11 14:43  StreamAzure  阅读(77)  评论(0编辑  收藏  举报