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;
}