首页 > 代码库 > poj2912(种类并查集+枚举)

poj2912(种类并查集+枚举)

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

 

题意:n个人进行m轮剪刀石头布游戏(0<n<=500,0<=m<=2000),接下来m行形如x, y, ch的输入,ch=‘=‘表示x, y平局,ch=‘>‘表示x赢y,ch=‘<‘表示x输y, 但是我们不知道x, y的手势是什么; 其中有一个人是裁判,它可以出任意手势,其余人手势相同的分一组,共分为三组,可以存在空组,也就是说除了裁判外,其余人每一次出的手势都相同,问能不能确定裁判是几号,如果能,输出最少在第几轮可以确定;

 

思路:感觉此题的题意有点模糊,加上样例才勉强看懂;如果能唯一确定裁判的编号,按照格式输出编号和能确定裁判的最少轮数,如果存在一个裁判但不能确定的话输出Can not determine,如果由输入得到有多个裁判或者一个裁判都没有,那么输出Impossible; 如果顺着题目的思路去想的话,因为裁判手势可以是任意的,很难确定编号之间的相对关系;又因为除了裁判外其余人的手势是不变的,那么我们一定可以将除裁判外的人分成三组(可以有空组);想到这里我们可以发现,如果我们假设某个编号为裁判,如果除了它外其余人的关系没有矛盾,那么它就可能是裁判;此题的数据不大, 那么只要我们枚举每一个节点,假设其为裁判,如果我们得到的可能为裁判的节点唯一,那么它就是裁判,如果得到可能为裁判的节点有多个,那么就不能确定裁判,如果可能为裁判的节点一个都没有,就是Impassible啦~

现在我们已经确定的了三种情况,剩下能确定裁判的时候还要输出最少几行能确定,我们是通过排除法来确定裁判编号的,枚举每个编号为裁判,一但在某一行输入中出现矛盾,我们就确定它不是裁判,那么n-1个出现矛盾的枚举中出现矛盾最晚的那个行数即为我们能确定裁判所需的最少行数啦(因为我们要排除n-1个编号才能确定裁判的编号嘛)~

 

代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #define MAXN 510
 5 using namespace std;
 6 
 7 int rank[MAXN], pre[MAXN]; //***rank数组存储当前节点与父亲节点的关系!注意不是与根节点的关系!!!
 8 
 9 int find(int x){//**递归路径压缩
10     if(pre[x]!=x){
11         int px=find(pre[x]);
12         rank[x]=(rank[x]+rank[pre[x]])%3; //***回溯时改变rank[x]的值,这个公式我们可以由枚举得出
13         pre[x]=px; //***将 x 的父亲节点变为根节点,即 x 节点直接指向根节点
14     }
15     return pre[x];
16 }
17 
18 int jion(int x, int y, int d){
19     int px=find(x);
20     int py=find(y);
21     if(px==py){  //***若 x, y 都已经加入并查集,即相对关系已经确定,递归压缩路径后 x, y 的根节点即为他们的父节点, 我们可以由rank得到 x, y的关系,因为我们加入并查集的 x, y的关系都是真确的,所以如果d值与我们由rank得到的关系不同,那么其为假话
22         if((rank[y]-rank[x]+3)%3!=d){ //***x, y的关系我们可以由枚举得出
23             return 1;
24         }
25     }else{
26         pre[py]=px;
27         rank[py]=(rank[x]-rank[y]+d+3)%3; //***得到合并后px与py的关系,此处的公式也是可以通过枚举得出的
28     }
29     return 0;
30 }
31 
32 int main(void){
33     int n, m;
34     while(~scanf("%d%d", &n, &m)){
35         int x[MAXN*4], y[MAXN*4], d;
36         char ch[MAXN*4];
37         for(int i=1; i<=m; i++){
38             scanf("%d%c%d", &x[i], &ch[i], &y[i]);
39         }
40         int tot=0, flag=1, gg=0, jj=0; //***tot统计裁判个数
41         for(int i=0; i<n; i++){   //***枚举,假设i为裁判,若无矛盾即可行
42             flag=1;
43             for(int j=0; j<n; j++){  //**初始化
44                 rank[j]=0;
45                 pre[j]=j;
46             }
47             for(int j=1; j<=m; j++){ //**建立关系并查集
48                 if(x[j]==i||y[j]==i){ //***裁判可以任意出手势,把裁判放在并查集外面
49                     continue;
50                 }
51                 if(ch[j]===){
52                     d=0;
53                 }else if(ch[j]==>){
54                     d=1;
55                 }else{
56                     d=2;
57                 }
58                 if(jion(x[j], y[j], d)){ //**判断是否有矛盾
59                     gg=max(j, gg); //**维护出现矛盾最大行数
60                     flag=0;
61                     break;
62                 }
63             }
64             if(flag){ //**没出现矛盾,即当前节点可以是裁判
65                 tot++;
66                 jj=i; //**记录裁判节点号
67             }
68         }
69         if(!tot){
70             printf("Impossible\n");
71         }else if(tot>1){
72             printf("Can not determine\n");
73         }else{
74             printf("Player %d can be determined to be the judge after %d lines\n", jj, gg);
75         }
76     }
77     return 0;
78 }

 

poj2912(种类并查集+枚举)