题意
自从Lele开发了Rating系统,他的Tetris事业更是如虎添翼,不久他遍把这个游戏推向了全球。
为了更好的符合那些爱好者的喜好,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"。
Input
本题目包含多组测试,请处理到文件结束。
每组测试第一行包含两个整数N,M(0<=N<=10000,0<=M<=20000),分别表示要排名的人数以及得到的关系数。
接下来有M行,分别表示这些关系
Output
对于每组测试,在一行里按题目要求输出
思路
本题是许多题目的升级版 , 比如这个题P1955 [NOI2015] 程序自动分析 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 但是明显,本题比这个题要复杂,但是还是有参考价值。
并查集只能维护集合内的一种关系 ,且这种关系具有传递律。
且 ----> 我们观察题目,发现其实不能使用三个并查集维护三种关系。因为题目还是需要判定其他的信息,并查集做不到。
但是对于题目,其实对于一组数据,它其实希望得到一组完全确定且唯一的关系,不是模棱两可的。 也就是这样 :
我们发现这是一组拓扑序列,因为我们给定许许多多的关系,但是最终这些关系的结点都可以被展开成唯一且合法序列。
但是从这样的一组关系中,没有发现一个等于的情况。其实互相等于的这些结点可以看成一个结点,也就是缩点。怎么去维护这样的信息呢 ? 使用并查集 维护互相相等的情况。所以我们需要先将等于的情况的约束全部建立完成。
然后对" > " , " < " 进行建图,这里我们将小于当前点的当作前驱结点 ,大于当前点的当作后继结点。 这里因为对于互相等于情况缩点了,我们采用并查集的根作为代表,代表当前这个集合内所有的点。
最后求top序列 , 这里就是判定的关键。 因为我们缩点了,所以最后 topsort 里面的点的数量需要减去被缩点的一些点。 然后将并查集的根是自己的,且入度为0的点加入到队列中。
- 如果队列中节点数大于 1 , 说明top序列不唯一 , 即关系不清楚。
- 如果图中存在环,那么一定存在点没有被加入到队列中 ,那么存在关系错误。
对于第二点的解释:假设结点 i , j之间存在环 , 对于结点i ,一定存在一个入度是来自j , 结点j一定存在一个入度是来自i。因为当结点入度为0的时候,才会将结点加入队列,拓展其他结点,但是我们成环必定是top序在后面的有一条边指向当前点,但是我们只有将当前点加入队列,才能遍历到导致成环的那个点的那条那边删掉,使得当前结点的入度为0 ,但是当前结点入度是不可能为0的。所以不可能加入队列。
这就类似于,难度1-8关 必须一关一关通过不能跳关递增 ,题目要求通过第八关才能通过第四关,但是第八关在第四关的后面,只有通过第四关才能通过第八关。
如果出现这样的情况,那么一定存在结点没有被遍历!
注:最后如果发现关系不明确也要继续做,因为可能出现关系不明确,且关系冲突的情况,这样输出冲突。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define LL long long
#define PII std::pair<int , int>
const int N = 10010;
int n , m;
std::vector<int> e[N];
int p[N] , in[N];
struct node{
int u , v;
char opt;
};
int find(int x){
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void solved(){
int sum = n;
for (int i = 0; i < n; i ++ ) e[i].clear() , p[i] = i , in[i] = 0; // 清空图 , 初始化并查集
std::vector<node> v;
while (m -- ){
int a , b;char opt;
std::cin >> a >> opt >> b;
if (opt == '='){ // 将 '=' 约束建完
a = find(a) , b = find(b);
if (a != b)
p[a] = b , sum --;
}else
v.push_back({a , b , opt});
}
for (auto q : v){
int x = find(q.u) , y = find(q.v); // 缩点
char opt = q.opt;
if (opt == '<')
e[x].push_back(y) , in[y] ++ ; // x ---> y
else
e[y].push_back(x) , in[x] ++ ;
}
std::queue<int> q;
for (int i = 0; i < n; i ++ )
if (find(i) == i && in[i] == 0)
q.push(i);
bool ok = 0;
while (q.size()){
if (q.size() > 1) ok = 1; // 说明 不明确
sum --;
int u = q.front();
q.pop();
for (int v : e[u])
if (-- in[v] == 0) q.push(v);
}
if (sum) std::cout << "CONFLICT\n";
else if (ok) std::cout << "UNCERTAIN\n";
else
std::cout << "OK\n";
}
int main(){
std::ios::sync_with_stdio(0);
std::cout.tie(0);
std::cin.tie(0);
while (std::cin >> n >> m)
solved();
return 0;
}