【并查集缩点+拓扑】Rank of Tetris
Rank of Tetris
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1827 Accepted Submission(s): 487
为了更好的符合那些爱好者的喜好,Lele又想了一个新点子:他将制作一个全球Tetris高手排行榜,定时更新,名堂要比福布斯富豪榜还响。关于如何排名,这个不用说都知道是根据Rating从高到低来排,如果两个人具有相同的Rating,那就按这几个人的RP从高到低来排。
终于,Lele要开始行动了,对N个人进行排名。为了方便起见,每个人都已经被编号,分别从0到N-1,并且编号越大,RP就越高。
同时Lele从狗仔队里取得一些(M个)关于Rating的信息。这些信息可能有三种情况,分别是"A > B","A = B","A < B",分别表示A的Rating高于B,等于B,小于B。
现在Lele并不是让你来帮他制作这个高手榜,他只是想知道,根据这些信息是否能够确定出这个高手榜,是的话就输出"OK"。否则就请你判断出错的原因,到底是因为信息不完全(输出"UNCERTAIN"),还是因为这些信息中包含冲突(输出"CONFLICT")。
注意,如果信息中同时包含冲突且信息不完全,就输出"CONFLICT"。
每组测试第一行包含两个整数N,M(0<=N<=10000,0<=M<=20000),分别表示要排名的人数以及得到的关系数。
接下来有M行,分别表示这些关系
0 > 1
1 < 2
0 > 2
4 4
1 = 2
1 > 3
2 > 0
0 > 1
3 3
1 > 0
1 > 2
2 < 1
解法:
蛮不错的一道经典题,不过题目有一点没理解好的是,当 A=B 的时候表示的是A和B的Rating是一样的,共用一个位置的(比如都在11名的话,他们两个的排名都是11,而不是一个11名一个12名),而他们两的相对位置和标号有关而已。也就是Rating不是唯一的,同一个Rating可能存在多个人,同一Rating的人则是按照编号大到小优先排序。
这道题目所要求的是,根据所给的信息,能否判断出是否能够构成Rating表。有三种情况,可行OK,信息不足UNCERTAIN,信息存在冲突CONFLICT。由于只要判断能否构建Rating表,所以,只需要判断能否拓扑即可,对于同一Rating的人顺序位置,不需要知道。在信息不冲突情况下,都能够拓扑排序即可、
因为Rating是不唯一的,可以把同一个Rating的人都看做一个人,也就是对所给的信息进行缩点,用并查集维护即可。如果一个人的Rating是一样的,当两个人在出现>或者<的时候,则可以判断信息出现冲突了。因为>或者<的出现表示两个数不可能是同一个Raring。在缩点之前,需要先把所有输入的信息先保存下来,先把Rating相等的点,进行缩点处理,再去判断输入的信息是否冲突。(PS:可以理解缩点后的Rating都是唯一的)
这一题的话,还需要判断重边和环的情况。当然,你用队列+邻接表来进行拓扑排序的话,则不需要进行这些处理了,详细见代码解释、
用队列进行拓扑排序的话,可以很好的判断三种条件:
1.当队列Q.size() > 1,表明所给的信息不足,因为有多个入度为0的点,无法判断先后顺序(不同Rating的人不是按编号排序的!!!)。
2.当队列结束后,剩余的缩点数n>0,说明出现环时,排名有冲突。
3.若进队列次数等于n,证明可以知道此图是单向无环图。
代码:2015.8.6(并查集缩点+拓扑+队列)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <map> 5 #include <queue> 6 #define MAX 10010 7 using namespace std; 8 int Fa[MAX]; 9 int InD[MAX]; 10 int First[MAX]; 11 struct edge{int TO,Next;}ID[30*MAX]; 12 int SIGN; 13 void Add_E(int x,int y) 14 { 15 ID[SIGN].TO=y;InD[y]++; 16 ID[SIGN].Next=First[x]; 17 First[x]=SIGN++; 18 } 19 void Cread(int N) 20 { 21 for(int i=1;i<=N;i++){First[i]=0;InD[i]=0;Fa[i]=i;} 22 } 23 int Find(int x) 24 { 25 if(x!=Fa[x])Fa[x]=Find(Fa[x]); 26 return Fa[x]; 27 } 28 int Link_P(int x,int y) 29 { 30 int A=Find(x); 31 int B=Find(y); 32 if(A!=B){Fa[A]=B;return 1;} 33 else return 0; 34 } 35 void ToPoSort(int N,int NUM)/*N为点数,NUM为剩余的缩点数*/ 36 { 37 int i,j,k,sign=0; 38 queue <int>Que; 39 for(i=1;i<=N;i++) 40 { 41 if(InD[i]==0&&i==Find(i))/*用并查集缩点则需要判断的是缩点*/ 42 Que.push(i); 43 } 44 while(!Que.empty()) 45 { 46 47 if(Que.size()>1)sign=1;/*存在两个入度为0的点,表示信息不完全*/ 48 k=Que.front();Que.pop(); 49 NUM--;InD[k]--; 50 for(i=First[k];i!=0;i=ID[i].Next)/*出现重边时候也会多减*/ 51 { 52 if(--InD[ID[i].TO]==0)/*将入度为0的点进队列*/ 53 { 54 Que.push(ID[i].TO); 55 } 56 } 57 } 58 if(NUM){printf("CONFLICT\n");}/*剩余的缩点数>0时,表示出现环时*/ 59 else if(sign){printf("UNCERTAIN\n");}/*信息不具体*/ 60 else {printf("OK\n");} 61 return ; 62 } 63 int main() 64 { 65 int M,N,i,T,k,j,NUM,A,B; 66 int a[MAX],b[MAX]; 67 char str[MAX]; 68 while(scanf(" %d %d",&N,&M)!=EOF) 69 { 70 Cread(N);NUM=N;SIGN=1;/*NUM记录缩点后的点数*/ 71 for(i=0;i<M;i++) 72 { 73 scanf(" %d %c %d",&a[i],&str[i],&b[i]); 74 a[i]++;b[i]++;/*变成编号为1~N的数*/ 75 if(str[i]=='=') 76 { 77 if(Link_P(a[i],b[i])) 78 NUM--;/*缩点后的数减1*/ 79 } 80 } 81 for(i=0,j=0;i<M;i++) 82 { 83 if(str[i]=='=')continue; 84 A=Find(a[i]);B=Find(b[i]); 85 if(A==B){j=1;break;} 86 if(str[i]=='>')Add_E(A,B); 87 else Add_E(B,A); 88 } 89 if(j){printf("CONFLICT\n");continue;} 90 ToPoSort(N,NUM); 91 } 92 return 0; 93 }
代码:2015.8.19(并查集缩点+拓扑+栈)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <stack> 5 #include <map> 6 #define MAX 10010 7 using namespace std; 8 int InD[MAX];/*InD[i]记录点i的入度*/ 9 int First[MAX];/*First[i]头结点的第一条边的编号*/ 10 struct edge 11 { 12 int TO;/*点*/ 13 int Next;/*下一条边的编号*/ 14 }ID[3*MAX]; 15 int SIGN; 16 int Fa[MAX]; 17 int Find(int x) 18 { 19 if(x!=Fa[x])Fa[x]=Find(Fa[x]); 20 return Fa[x]; 21 } 22 int Link_P(int a ,int b) 23 { 24 int A=Find(a); 25 int B=Find(b); 26 if(A!=B){Fa[A]=Fa[B];return 1;} 27 else return 0; 28 } 29 30 void Add_E(int x,int y)/*添加点操作*/ 31 { 32 ID[SIGN].TO=y; 33 InD[y]++; 34 ID[SIGN].Next=First[x]; 35 First[x]=SIGN++; 36 } 37 int Jude(int x,int y)/*查找与X是否与Y相连*/ 38 { 39 int i; 40 for(i=First[x];i!=0;i=ID[i].Next) //查找与该点相关的点 41 { 42 if(ID[i].TO==y)return 0; 43 } 44 return 1; 45 } 46 void ToPoSort(int N,int Num[],int NNN)/*拓扑排序,邻接表*/ 47 { 48 int i,j,jj,k,sign=0,NN=N-NNN; 49 int KK,P; 50 stack<int>s; 51 for(i=1;i<=N;i++) 52 if(Find(i)==i&&InD[i]==0) 53 s.push(i); 54 while(!s.empty()) 55 { 56 if(s.size()>1)sign=1; 57 k=s.top();s.pop(); 58 NN--;InD[k]--; 59 for(i=First[k];i!=0;i=ID[i].Next) 60 { 61 if(--InD[ID[i].TO]==0) 62 s.push(ID[i].TO); 63 } 64 } 65 if(NN){printf("CONFLICT\n");}/*剩余的缩点数>0时,表示出现环时*/ 66 else if(sign){printf("UNCERTAIN\n");}/*信息不具体*/ 67 else {printf("OK\n");} 68 } 69 int main() 70 { 71 int M,N,i,T_T,NNN; 72 int a[MAX],str[MAX],b[MAX]; 73 int Num[MAX]; 74 while(scanf("%d%d",&N,&M)!=EOF) 75 { 76 for(i=1;i<=N;i++){First[i]=0;InD[i]=0;Fa[i]=i;} 77 for(i=1,NNN=0;i<=M;i++) 78 { 79 scanf("%d %c %d",&a[i],&str[i],&b[i]); 80 a[i]++;b[i]++; 81 if(str[i]=='=') 82 { 83 if(Link_P(a[i],b[i])) 84 NNN++; 85 } 86 } 87 for(i=1,SIGN=1,T_T=0;i<=M;i++) 88 { 89 if(str[i]=='=')continue; 90 else 91 { 92 int A=Find(a[i]),B=Find(b[i]); 93 if(A==B){T_T=1;break;} 94 else 95 { 96 if(str[i]=='>') 97 {if(Jude(A,B))Add_E(A,B);} 98 else 99 {if(Jude(B,A))Add_E(B,A);} 100 } 101 } 102 } 103 if(T_T){printf("CONFLICT\n");continue;} 104 ToPoSort(N,Num,NNN); 105 } 106 return 0; 107 }
PS:虽然是第二次做,刻意换了种方法做,还是多多少少有些细节错误,在用并查集缩点的时候,在添加边的操作的时候,有忘记用缩点后的并查集来代替了的,Orz,Orz,Orz.....
提供一组缩点不注意就容易出错的数据:
/*
3 3
0>1
2>0
1=2
*/
**************************************
* 作者: Wurq
* 博客: https://www.cnblogs.com/Wurq/
* Gitee: https://gitee.com/wurq
**************************************