POJ[2912] Rochambeau 【边带权并查集+枚举】

Description

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

题目大意:

n个人进行m轮剪刀石头布游戏(0<n<=500,0<=m<=2000),我们知道每轮游戏的两人的编号x, y和结果,但是我们不知道x, y的手势是什么; 其中有一个人是裁判,他可以出任意手势。其余人手势相同的分一组,共分为三组,可以存在空组,也就是说除了裁判外,其余人每一次出的手势都相同,问能不能确定裁判是几号,如果能,输出最少在第几轮可以确定。

分析:

首先,要分析一下不同情况下的输出。

1、如果能唯一确定裁判的编号,按照格式输出编号和能确定裁判的最少轮数;

2、如果存在多个可能的裁判但不能确定的话,输出Can not determine;

3、如果由输入无法得到任何一个裁判的候选,那么输出Impossible; 

由于裁判每次出的手势是随机的,那么就无法将他固定在某一个组中。而除去裁判,其他人能够确定地被分成三组。鉴于本题的数据规模较小,我们枚举每一个编号,尝试将它作为裁判。那么如果除了他外其余人的关系没有矛盾,那么他就可能是裁判。

这样,我们就能够判断三种不同的情况。但对于第一种情况,还需要确定最少需要多少行的结果,才能确定该裁判。在我们枚举的过程中,一但在某一行输入中出现矛盾,我们就确定该编号不是裁判,那么n-1个出现矛盾的枚举中出现矛盾最晚的那个行数即为我们能确定裁判所需的最少行数啦(到达这一行,意味着前面的所有行已经能够将其他n-1个编号全部排除,即他们都不可能是裁判)。

在枚举的过程中,我们采用边带权的并查集维护各编号之间的关系。

0——父节点和子节点属于同一组

1——父节点输于子节点

2——父节点赢于子节点

所有操作均在模3的意义下进行。

首先,子节点、父节点和爷节点的关系:d[x](子到爷的关系)=(d[x](子到父的关系)+d[y](父到爷的关系))%3。这只要枚举各种情况就会得出。

然后,路径压缩:d[x](3到1的关系)=d[x](3到2的关系)+d[y](2到1的关系))%3。

当我们求任意两个编号x, y的关系时,先求他们的根节点。

1)若两者根节点相同,说明我们已经赋予x,y关系了,只需要验证题中给的关系符合不符合我们已有的关系即可。

此时知道d[x]与d[y],则x到y的关系是:(d[x]+(3-d[y]))%3(根据向量的关系,d[y] 的反向向量权值为3-d[y],可以枚举验证一下)即判断x到y的关系与题目所给相等。

2)若两者根节点不相同,说明我们还没有赋予x,y关系,令root1=find(x),root2=find(y);fa[root2]=root1,关键就是怎么求d[root2]。

如图,此时求的是e1,  d1=a1+b1 ;  e1=d1-c1 ; e1=(a1+b1-c1);

a1=d[x] ; c1=d[y] ; b1=rel;(题上给的x,y的关系)

而-c1=(3-rel[y]) %3,之前已经给出过了。

综上:e1=( d[x]+rel+(3-d[y])  )%3

即d[root2]=(d[x]+rel+3-d[y])%3。

具体解释见代码。

#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <cstring>
#include <string>
#include <algorithm>

using namespace std;
const int maxn=505;

int n,m;

int fa[maxn],d[maxn];

struct node{
	int x,y;
	char c;
};

vector<node> res;
vector<int> judge;

void init(){
	for(int i=0;i<=n;i++){
		fa[i]=i;
		d[i]=0;
	}
}

int find(int x){
	if(fa[x]==x)  return x;
	int root=find(fa[x]);
	d[x]=(d[fa[x]]+d[x])%3;		//路径压缩 
	return fa[x]=root;
}

int main(){
	char str[25];
	int x,y,rel;
	char c;
	while(cin>>n>>m){
		res.clear();
		judge.clear();
		cin.get();
		for(int i=1;i<=m;i++){
			cin.getline(str,25);
			istringstream in(str);
			node tmp;
			in>>tmp.x>>tmp.c>>tmp.y;
//			cout<<tmp.x<<"  "<<tmp.c<<"  "<<tmp.y<<endl;
			res.push_back(tmp);
		}
		int lst=0;
		for(int i=0;i<n;i++){
			init();		//记得每次枚举都要初始化 
			int flag=0;
			for(int j=0;j<res.size();j++){
				x=res[j].x;
				y=res[j].y;
				c=res[j].c; 
				if(x==i||y==i)  continue;
				if(c=='=')  rel=0;		//父节点和子节点属于同一组 
				else if(c=='<')  rel=1;		//父节点输于子节点 
				else  rel=2;		//父节点赢于子节点 
				
				int fx=find(x),fy=find(y);
				if(fx==fy){
					if((3-d[x]+d[y])%3==rel)  continue;		//同一集合中两者关系的计算 
					else{		//出现矛盾 
						flag=1;
						lst=max(lst,j+1);		//维护出现矛盾的最大行数 
						break;
					}
				}
				else{		//合并操作 
					fa[fy]=fx;
					d[fy]=(d[x]+rel+3-d[y])%3;
				}
			}
			if(flag==0)  judge.push_back(i);		//记录可能的裁判编号 
		}
//		cout<<judge.size()<<endl;
		if(judge.size()==1){
			cout<<"Player "<<judge[0]<<" can be determined to be the judge after "<<lst<<" lines"<<endl;
		}
		else if(judge.size()==0){
			cout<<"Impossible"<<endl;
		}
		else{
			cout<<"Can not determine"<<endl;
		}
	}
	return 0;
} 
发布了30 篇原创文章 · 获赞 5 · 访问量 900

猜你喜欢

转载自blog.csdn.net/qq_42840665/article/details/102655895