算法竞赛进阶指南 0x49(并查集) 石头剪子布

题面

算法竞赛进阶指南 0x49(并查集) 石头剪子布_第1张图片

题解

算法竞赛进阶指南 0x49(并查集) 石头剪子布_第2张图片

我用的是带边权的并查集,那么就要维护到祖宗节点的距离,我们用上图的关系来表示两个人之间的关系(d[x]-d[y])%3==1 说明 x > y

题中说还有裁判,那么我们就枚举裁判,然后开始判断条件做并查集,遇到有关裁判的,直接跳过,因为裁判可以任意出,一定是没有矛盾的,否则,如果两个人不在一个集合,就按大小关系合并,然后维护到祖宗节点的距离;对于在一个集合中的,就判断是否与之前矛盾,如果与之前有矛盾,说明枚举的裁判不符合,跳出循环枚举下一个,如果所有条件都符合,就说明这个人可以是裁判,记录一下即可

算法竞赛进阶指南 0x49(并查集) 石头剪子布_第3张图片

这里我们要提前处理一下输入,将 < 关系全部变成 > 关系 (0<1 变成 1>0),未为了后边判断方便,注意字符转数字,这里改了好久没看出来,还有>10的字符转数字 ,其他就是并查集的板子了

代码

#include
#include
#include
#include
#include

using namespace std;
const int N = 510, M = 2100;

int n, m;
int p[N], d[N];
int in[M][2];
char op[M];


//初始化
void init() {
    for (int i = 0; i <= N; i++) {
        p[i] = i;
        d[i] = 0;
    }
}


//返回x的祖宗节点
int find(int x) {
    if (p[x] != x) {
        int root = find(p[x]);
        d[x] += d[p[x]];
        p[x] = root;
    }
    return p[x];
}

//处理输入
void pre() {
    for (int i = 1; i <= m; i++) {
        string s;
        cin >> s;
        string a, b;
        int index = 0;
        for (int j = s.size() - 1; j >= 0; j--) {
            if (s[j] >= '0' && s[j] <= '9') {
                b += s[j];
            } else {
                index = j;
                break;
            }
        }
        for (int j = index - 1; j >= 0; j--) {
            a += s[j];
        }
        int x1 = 0, x2 = 0;
        int t = 1;
        for (int j = 0; j < a.size(); j++) {
            x1 += t * (a[j] - '0');
            t *= 10;
        }
        t = 1;
        for (int j = 0; j < b.size(); j++) {
            x2 += t * (b[j] - '0');
            t *= 10;
        }
        if (s[index] == '<') {
            in[i][0] = x2;
            in[i][1] = x1;
            op[i] = '>';
        } else {
            in[i][0] = x1;
            in[i][1] = x2;
            op[i] = s[index];
        }
        //cout << in[i][0] << op[i] << in[i][1] << endl;
    }


}


int main() {

    while (cin >> n >> m) {
        pre();   //处理输入
        //特判
        if (n == 1) {
            cout << "Player 0 can be determined to be the judge after 0 lines" << endl;
            continue;
        }
        if (m == 0) {
            cout << "Can not determine" << endl;
            continue;
        }

        int cnt = 0;//记录可能的裁判数
        int tm = 0;//最晚的冲突时刻
        int ans = 0;//记录裁判下标
        for (int k = 0; k < n; k++) {  //枚举裁判
            init();  //初始化
            int i;
            for (i = 1; i <= m; i++) {   //输入条件判断
                int x = in[i][0], y = in[i][1];
                int px = find(x), py = find(y);
                if (x == k || y == k) continue;  //遇到裁判跳过
                if (px != py) {  //不在同一个集合,合并
                    if (op[i] == '=') {
                        p[px] = py;//将x集合合并到y集合中
                        d[px] = d[y] - d[x];  //维护距离是同类
                    } else {   //是 '>'
                        p[px] = py;
                        d[px] = d[y] + 1 - d[x];
                    }
                } else {  //在同一个集合,判断
                    int len1 = (d[x] - d[y] - 1) % 3;
                    int len2 = (d[x] - d[y]) % 3;
                    if ((len1 == 0 && op[i] == '>') || (len2 == 0 && op[i] == '=')) {
                    } else {
                        tm = max(tm, i);
                        break;
                    }
                }
            }
            if (i == m + 1) {   //说明i可以是裁判
                cnt++;
                ans = k;
            }

        }

        if (!cnt) {
            cout << "Impossible" << endl;
        } else if (cnt > 1) {
            cout << "Can not determine" << endl;
        } else {
            cout << "Player " << ans << " can be determined to be the judge after " << tm << " lines" << endl;
        }
    }


    return 0;
}

你可能感兴趣的:(#,并查集,并查集)