题意
题解
将选手拆分为 3 3 3 个点,分别代表选手的出招属于剪刀、石头和布。使用并查集维护上述关系。裁判可以任意出招,将其参与的比赛合并进并查集会产生逻辑错误。
考虑枚举裁判,不考虑其参与的比赛,若产生关系冲突则代表当前枚举的选手不是裁判,记录可以将其排除出备选裁判的时间点。若最终没有备选裁判,则比赛出现了不可能的情况;若存在多个备选裁判,则不能判断;其余情况,可以判断,最短判断时间为排除其余选手的时间点的最大值。
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 505, maxm = 2005;
int N, M, X[maxm], Y[maxm], fa[maxn * 3], sz[maxn * 3];
int tot, mn, P[maxn];
char op[maxm];
int find(int x) { return fa[x] == x ? x : (fa[x] = find(fa[x])); }
bool same(int x, int y) { return find(x) == find(y); }
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y)
return;
if (sz[x] < sz[y])
swap(x, y);
fa[y] = x, sz[x] += sz[y];
}
int main()
{
while (~scanf("%d%d", &N, &M))
{
for (int i = 1; i <= M; ++i)
scanf("%d%c%d", X + i, op + i, Y + i);
tot = mn = 0;
for (int i = 0; i < N; ++i)
{
for (int j = 0, k = N * 3; j < k; ++j)
fa[j] = j, sz[j] = 1;
bool f = 1;
for (int j = 1; j <= M; ++j)
{
int x = X[j], y = Y[j];
if (x == i || y == i)
continue;
int xa = x, xb = x + N, xc = x + (N << 1);
int ya = y, yb = y + N, yc = y + (N << 1);
if (op[j] == '=')
{
if (same(xa, yb) || same(xa, yc))
{
mn = max(mn, j), f = 0;
break;
}
unite(xa, ya), unite(xb, yb), unite(xc, yc);
}
else if (op[j] == '>')
{
if (same(xa, ya) || same(xa, yb))
{
mn = max(mn, j), f = 0;
break;
}
unite(xa, yc), unite(xb, ya), unite(xc, yb);
}
else
{
if (same(xa, ya) || same(xa, yc))
{
mn = max(mn, j), f = 0;
break;
}
unite(xa, yb), unite(xb, yc), unite(xc, ya);
}
}
if (f)
P[++tot] = i;
}
if (!tot)
puts("Impossible");
else if (tot == 1)
printf("Player %d can be determined to be the judge after %d lines\n", P[1], mn);
else
puts("Can not determine");
}
return 0;
}