[poj 2912] Rochambeau 解题报告 (带权并查集)

时间:2020-12-07 16:08:01

题目链接:http://poj.org/problem?id=2912

题目:

[poj 2912] Rochambeau 解题报告 (带权并查集)

题目大意:

n个人进行m轮剪刀石头布游戏(0<n<=500,0<=m<=2000)

接下来m行形如x,y,ch的输入,ch='='表示x,y平局, ch='>'表示x赢y,ch='<'表示x输y, 但是我们不知道x,y的手势是什么;

其中有一个人是裁判,它可以出任意手势,其余人手势相同的分一组,共分为三组,可以存 在空组。

也就是说除了裁判外,其余人每一次出的手 势都相同,问能不能确定裁判是几号,如果能,输出 最少在第几轮可以确定;

题解:

带权并查集

我们考虑枚举裁判,然后对每轮进行处理,包含当前枚举的裁判的轮我们直接跳过

二者之前的关系我们用0(=),1(>),2(<)表示,记得%3,用val数组记录当前点到所在并查集根节点的关系

对于每轮,我们首先判断是否在同一联通块里。如果在,判断当前关系是否符合,即val[b]是否等于val[a]+ch(我们定义ch是b连向a的),如果是就继续,不是的话说明当前的点不是裁判;如果不在,就带权合并一下就好了

那么最少的发现裁判的轮数呢?我们发现我们实际上在做一个排除法,那么我们最晚排除的那个人的轮数就是最少的发现裁判的轮数

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std; const int N=+;
const int M=+;
int n,m;
int fa[N],a[M],b[M],ch[M],val[N];
int find(int x)
{
if (fa[x]==-) return x;
int tmp=find(fa[x]);
val[x]+=val[fa[x]];
val[x]%=;
return fa[x]=tmp;
}
int main()
{
while (~scanf("%d%d",&n,&m))
{
char c[];
for (int i=;i<=m;i++)
{
scanf("%s",c+);
a[i]=b[i]=;
int len=strlen(c+);
int l=;
while (c[l]>=''&&c[l]<='') a[i]=(a[i]<<)+(a[i]<<)+c[l]-'',l++;
if (c[l]=='=') ch[i]=;
if (c[l]=='>') ch[i]=;
if (c[l]=='<') ch[i]=;
l++;
while (c[l]>=''&&c[l]<=''&&l<=len) b[i]=(b[i]<<)+(b[i]<<)+c[l]-'',l++;
//printf("%d %d %d\n",a[i],ch[i],b[i]);
}
int round=,judger=-;
bool mark=false;
for (int i=;i<n;i++)
{
bool flag=false;
memset(fa,-,sizeof(fa));
memset(val,,sizeof(val));
for (int j=;j<=m;j++)
{
if (a[j]==i||b[j]==i) continue;
int fx=find(a[j]),fy=find(b[j]);
if (fx==fy)
{
if (val[b[j]]!=(val[a[j]]+ch[j]+)%)
{
round=max(round,j);
flag=true;
break;
}
}
else
{
fa[fy]=fx;
val[fy]=(val[a[j]]+ch[j]-val[b[j]]+)%;
}
}
if (!flag)//当前的是法官
{
if (judger==-)
{
judger=i;
}
else
{
mark=true;
break;
}
}
}
if (mark) printf("Can not determine\n");
else if (judger==-) printf("Impossible\n");
else printf("Player %d can be determined to be the judge after %d lines\n",judger,round);
}
return ;
}