【并查集】POJ 2912 Rochambeau

POJ 2912 : Rochambeau

题意

n个小朋友进行石头剪刀布的游戏,n个小朋友分为3组,每组里面的人出的手势都是一样的。但是其中有一个裁判,裁判可以出任意的手势。如果只有一个裁判则输出最迟在第几行能看出玩家几是裁判,如果有多个可以出任意手势的则输出Can not determine,如果没有裁判则输出Impossible。

思路

这里实际上是应用了带关系的并查集操作。对于每个小朋友 ai a i ,我们除去和 ai a i 相关的,然后判断剩下的操作是否符合事实。如果符合则证明 ai a i 是裁判。
然后看看我们枚举其他人时,比如我们假设第一个人是裁判时,第二局出错了,那说明第二句就断定了第一个人不是裁判。
以此来算,我们找到裁判后,【记录】其他所有的出错局数最大的就是我们判断的步数;因为到了这一步,可以断定其他人都不是裁判了。

细节

(1)对于”<” 和 “>” 操作可以理解为”吃”的关系,即为x < < y代表x吃y,y > > x代表x被y吃。
(2)对于”=”可以理解为同类的关系,即为x=y代表x和y是同类。
(3)如果是同类,则x不能吃y,y不能吃x
(4)如果x吃y成立,则x和y不会是同类,或者y不能吃x

代码

#include <iostream>
#include <cstdio>
#include <map>
#include <string.h>
using namespace std;

const int MAXNUM = 3 * (1000 + 10);
int id[MAXNUM];
int Size[MAXNUM];
// 初始化
void make_set(int n){
    for(int i = 0 ; i < n ; i++){
        id[i] = i;
        Size[i] = 1;
    }
}

// 查找父节点
int Find(int p) {
     while (p != id[p]) {
        // 路径压缩,会破坏掉当前节点的父节点的尺寸信息,因为压缩后,当前节点的父节点已经变了
        id[p] = id[id[p]];
        p = id[p];
    }
    return p;
}

// 合并 p ,q节点
void Union(int p, int q) {
    int pRoot = Find(p);
    int qRoot = Find(q);

    if (pRoot == qRoot) {
        return;
    }
    // 按秩进行合并
    if (Size[pRoot] > Size[qRoot]) {
        id[qRoot] = pRoot;
        Size[pRoot] += Size[qRoot];
    } else {
        id[pRoot] = qRoot;
        Size[qRoot] += Size[pRoot];
    }
}
int main(){
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    int n, m ;
    int x[MAXNUM], y[MAXNUM];
    char c[MAXNUM];
    int flag[MAXNUM];
    while(scanf("%d %d", &n , &m)!=EOF){
        for(int i = 0 ; i < m; i++){
            scanf("%d%c%d",&x[i],&c[i],&y[i]);
        }
        int cnt = 0;
        // player记录为裁判,Lastlines记录最迟在哪一步确定其他人都不是裁判
        int Lastlines = 0 ,player;
        int d;
        for(int i = 0 ; i < n ; i++){
            memset(flag, 0 , sizeof(flag));
            int f = 0;
            make_set(3 * n);

            for(int j = 0; j < m ; j++){
                // 除去裁判
                if(x[j] == i || y[j] == i)
                    continue;

                int p , q;
                if(c[j] == '<'){
                    p = x[j]; q = y[j]; d = 1;
                }else if (c[j] == '>'){
                    p = y[j]; q = x[j]; d = 1;
                }else{
                    p = x[j]; q = y[j]; d = 0;
                }
                // 如果同类,则p不能吃q,q不能吃p
                if(d == 0){
                    if(Find(p) == Find(q + n) ||Find(p) == Find(q + 2 * n)){
                        f = 1;
                        if(j + 1 > Lastlines)
                            Lastlines = j + 1;
                        break;
                    }
                    else{
                        Union(p, q);
                        Union(p + n, q + n);
                        Union(p + 2 * n, q + 2 * n);
                    }
                }
                // 如果p吃q成立,则p和q不会是同类,或者q不能吃p
                if(d == 1){
                    if(Find(p) == Find(q) ||Find(p) == Find(q + 2 * n)){
                        f = 1;
                        if(j + 1 > Lastlines)
                            Lastlines = j + 1;
                        break;
                    }else{
                        Union(p , q + n);
                        Union(p + n , q + 2 * n);
                        Union(p + 2 * n , q);
                    }
                }

            }
            // 完全符合
            if(f == 0){
                cnt++;
                // 记录裁判
                player = i;
            }

        }
        // 没有裁判
        if(cnt == 0)
            printf("Impossible\n");
        else if(cnt == 1)
            printf("Player %d can be determined to be the judge after %d lines\n",player, Lastlines);
        else // 多个裁判
            printf("Can not determine\n");

    }

    return 0;
}

代码

#include <iostream>
#include <cstdio>
#include <map>
#include <string.h>
using namespace std;

const int MAXNUM = 20000 + 10;
int id[MAXNUM];
int Size[MAXNUM];
int r[MAXNUM];
// 初始化
void make_set(int n){
    for(int i = 0 ; i < n ; i++){
        id[i] = i;
        Size[i] = 1;
        r[i] = 0;
    }
}

// 查找父节点
int Find(int p) {
    while (id[p] != id[id[p]]) {   //如果q不是其所在子树的根节点的直接孩子
         r[p] = (r[p] + r[id[p]] ) % 3;
         id[p] = id[id[p]];          //对其父节点到其爷爷节点之间的路径进行压缩
      }
    return id[p];
}

// 合并 p ,q节点
void Union(int p, int q, int d) {
    int pRoot = Find(p);
    int qRoot = Find(q);

    if (pRoot == qRoot) {
        return;
    }
    id[qRoot] = pRoot;
    r[qRoot] = (r[p] - r[q] + 3 + (d - 1)) % 3;
}

int main(){
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    int n, m ;
    int x[MAXNUM], y[MAXNUM];
    char c[MAXNUM];
    while(scanf("%d %d", &n , &m)!=EOF){
        for(int i = 0 ; i < m; i++){
            scanf("%d%c%d",&x[i],&c[i],&y[i]);
        }
        int cnt = 0;
        // player记录为裁判,Lastlines记录最迟在哪一步确定其他人都不是裁判
        int Lastlines = 0 ,player;
        int d;
        for(int i = 0 ; i < n ; i++){
            int f = 0;
            make_set(n);

            for(int j = 0; j < m ; j++){
                // 除去裁判
                if(x[j] == i || y[j] == i)
                    continue;

                int p , q;
                if(c[j] == '<'){
                    p = x[j]; q = y[j]; d = 2;
                }else if (c[j] == '>'){
                    p = y[j]; q = x[j]; d = 2;
                }else{
                    p = x[j]; q = y[j]; d = 1;
                }


                if(Find(p) == Find(q)){
                    if( (d == 1 && r[p] != r[q] ) ||(d == 2 && (r[p] + 1) % 3 != r[q]) ){
                        f = 1;
                        if(j + 1 > Lastlines)
                            Lastlines = j + 1;
                        break;
                    }
                }else{
                     Union(p ,q ,d);
                }
            }
            // 完全符合
            if(f == 0){
                cnt++;
                // 记录裁判
                player = i;
            }

        }
        // 没有裁判
        if(cnt == 0)
            printf("Impossible\n");
        else if(cnt == 1)
            printf("Player %d can be determined to be the judge after %d lines\n",player, Lastlines);
        else // 多个裁判
            printf("Can not determine\n");

    }

    return 0;
}
点赞