【带权并查集+枚举】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;
}