题意:有n个人玩石头剪刀布,有且只有一个裁判。除了裁判每个人的出拳形式都是一样的。
a<b表示b打败a,a=b表示a和b出拳一样,平手。a>b表示a打败b。
给出m个回合的游戏结果,问能否判断出谁是裁判?如果能还要输出是在哪个回合之后判断出谁是裁判。
分析:枚举和加权并查集+路径压缩。
对于每个人假设其为裁判,然后去掉所有和他有关的匹配,判断是否会出现矛盾。
w[i]=0:i和根节点属于同一集合;w[i]=1:根节点打败i;w[i]=2:i打败根节点
在寻找根节点的find()函数中,w的更新函数是:w[x]=(w[x]+w[s[x]])%3;
举个例子:找到根节点之前w[x]=1,val[s[x]]=2:表示父节点打败x,父节点也打败父节点的父节点。
(注意此时w[x]是x与父节点的关系)
所以按照递归的思路,从递归倒数第二层开始s[x]就表示为根节点了,那么pre[x]打败根节点。
又因为s[x]也打败x所以w[x]=0=(1+2)%3;递归在往上一层时pre[x]又表示为根节点了。
再来看看合并操作时的w关系。
fa,fb分别为aa,bb的根节点,ww是aa与bb的关系。
当fa!=fb时,令s[fa]=fb.
假设w[aa]=1,即fa打败aa①,w[bb]=2,即bb打败fb②,ww=1,即bb打败aa③。
则由②③的aa和fb是同一集合。再由①得fa打败fb。即w[fa]=2.
也就是w[fa]=(w[bb]-w[aa]+ww)%3,
但是由于有可能w[bb]-w[aa]+ww<0,所以正确的方程是:w[fa]=(w[bb]-w[aa]+ww+3)%3,
当fa==fb时,那么就要判断是否出现矛盾,如果出现矛盾那么说明i不能作为裁判。
判断矛盾是:(w[aa]-w[bb]+3)%3和ww是否相等。
如果不矛盾,那么就接着读入输入到最后。
还要注意一点就是可能会出现多个裁判,那就是Can not determine。
N children are playing Rochambeau (scissors-rock-cloth) game with you. One of them is the judge. The rest children are divided into three groups (it is possible that some group is empty). You don’t know who is the judge, or how the children are grouped. Then the children start playing Rochambeau game for M rounds. Each round two children are arbitrarily selected to play Rochambeau for one once, and you will be told the outcome while not knowing which gesture the children presented. It is known that the children in the same group would present the same gesture (hence, two children in the same group always get draw when playing) and different groups for different gestures. The judge would present gesture randomly each time, hence no one knows what gesture the judge would present. Can you guess who is the judge after after the game ends? If you can, after how many rounds can you find out the judge at the earliest?
Input
Input contains multiple test cases. Each test case starts with two integers N and M (1 ≤ N ≤ 500, 0 ≤ M ≤ 2,000) in one line, which are the number of children and the number of rounds. Following are M lines, each line contains two integers in [0, N) separated by one symbol. The two integers are the IDs of the two children selected to play Rochambeau for this round. The symbol may be “=”, “>” or “<”, referring to a draw, that first child wins and that second child wins respectively.
Output
There is only one line for each test case. If the judge can be found, print the ID of the judge, and the least number of rounds after which the judge can be uniquely determined. If the judge can not be found, or the outcomes of the M rounds of game are inconsistent, print the corresponding message.
Sample Input
3 3
0<1
1<2
2<0
3 5
0<1
0>1
1<2
1>2
0<2
4 4
0<1
0>1
2<3
2>3
1 0
Sample Output
Can not determine
Player 1 can be determined to be the judge after 4 lines
Impossible
Player 0 can be determined to be the judge after 0 lines
题意分析:简单来说,就是,m个人中有且只有一个裁判,每次枚举一个人不参与出拳,看是否出现矛盾,若有,
则有且只有m-1次出现矛盾才能找到裁判。
AC代码分步详细分解
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define M 500+10
#define N 2000+10
struct node///开结构体,记录每一行u,v和他们之间的关系k
{
int u,v,k;
} q[N];
int s[M];///s[x]并查集记录x的父节点s[x]
int w[M];///w[x]两节点的关系权值0,1,2,表示x和根节点的关系
int dp[M];///记录出现矛盾的行数
int n,m;
int Find(int x)/*找到x的父节点*/
{
if(x==s[x])
return x;
int temp=s[x];
s[x]=Find(temp);
w[x]=(w[x]+w[temp])%3; ///回溯更新与父节点的关系
return s[x];
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
int i,j,k,u,v;
char e;
for(i=1; i<=m; ++i)
{
scanf("%d%c%d",&q[i].u,&e,&q[i].v);
if(e=='>')// y吃x
q[i].k=2;
else if(e=='<') // x吃y
q[i].k=1;
else if(e=='=') //同类
q[i].k=0;
}
memset(dp,-1,sizeof(dp));
for(i=0; i<n; i++)///枚举每个人
{
memset(w,0,sizeof(w));
for(int i=0; i<n; ++i)
s[i]=i;//注意这里要初始化,因为每次建立的关系都有可能不一样
for(j=1; j<=m; ++j)
{
if(q[j].u==i||q[j].v==i) ///枚举:屏蔽裁判
continue;
u=q[j].u,v=q[j].v,k=q[j].k;///k为u,v之间的关系
int fx=Find(u),fy=Find(v);///fx,fy分别 u,v的根节点
if(fx!=fy)///如果两个节点的boss不是一个人,那么就要建立关系。
{/**起始w均为零,则k的关系,即为u,v之间的关系*/
s[fy]=fx; /**单纯表示连通,fx(u的父节点)放在左子树上,fy(v的父节点)为根,u的父节点与v的父节点关系由w[fy]记录*/
w[fy]=(w[u]+k+3-w[v])%3;///(w[u]表示父节点fx与u的关系)(w[v]表示fy与v的关系)由k的值判定u与v的关系
}///但是由于有可能 w[u]+k+-w[v]<0,所以正确的方程是: w[fy]=(w[u]+k+3-w[v])%3,
else///如果两个节点boss相同就要考虑ab的关系权值是否与节点间原来的关系权值是一样的,如果不一样,那么这个人就不是裁判,同时记录下错是错在第多少组数据
{
if((w[v]+3-w[u])%3!=k)//出现矛盾
{
dp[i]=j;//标记出现矛盾所在行
break;
}
}
}
}
int cnt=0,ans=0;
for(i=0; i<n; ++i)
{
if(dp[i]==-1)
cnt++,v=i;
ans=max(ans,dp[i]);///判断边最大的矛盾边(即此时才可以保证当枚举去除任何一个人,都可以发现矛盾)
}
if(!cnt)
printf("Impossible\n");
else if(cnt>1)
printf("Can not determine\n");
else if(cnt==1)/**即当枚举去除裁判,有且只有当去除真正的裁判,才不会发生矛盾*/
printf("Player %d can be determined to be the judge after %d lines\n",v,ans);
}
return 0;
}