hdu1811 Rank of Tetris(并查集+拓扑排序)

52 阅读5分钟

题意

题目链接

自从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) 但是明显,本题比这个题要复杂,但是还是有参考价值

并查集只能维护集合内的一种关系 ,且这种关系具有传递律

<a,b><a , b><b,c><b , c> ----> <a,c><a , c> 我们观察题目,发现其实不能使用三个并查集维护三种关系。因为题目还是需要判定其他的信息,并查集做不到。

但是对于题目,其实对于一组数据,它其实希望得到一组完全确定且唯一的关系,不是模棱两可的。 也就是这样 :

a>b>c>d>ea > b > c > d > e

我们发现这是一组拓扑序列,因为我们给定许许多多的关系,但是最终这些关系的结点都可以被展开成唯一且合法序列。

但是从这样的一组关系中,没有发现一个等于的情况。其实互相等于的这些结点可以看成一个结点,也就是缩点。怎么去维护这样的信息呢 ? 使用并查集 维护互相相等的情况。所以我们需要先将等于的情况的约束全部建立完成。

然后对" > " , " < " 进行建图,这里我们将小于当前点的当作前驱结点 ,大于当前点的当作后继结点。 这里因为对于互相等于情况缩点了,我们采用并查集的根作为代表,代表当前这个集合内所有的点。

最后求top序列 , 这里就是判定的关键。 因为我们缩点了,所以最后 topsort 里面的点的数量需要减去被缩点的一些点。 然后将并查集的根是自己的,且入度为0的点加入到队列中。

  • 如果队列中节点数大于 1 , 说明top序列不唯一 , 即关系不清楚。
  • 如果图中存在环,那么一定存在点没有被加入到队列中 ,那么存在关系错误。

对于第二点的解释:假设结点 i , j之间存在环 , 对于结点i ,一定存在一个入度是来自j , 结点j一定存在一个入度是来自i。因为当结点入度为0的时候,才会将结点加入队列,拓展其他结点,但是我们成环必定是top序在后面的有一条边指向当前点,但是我们只有将当前点加入队列,才能遍历到导致成环的那个点的那条那边删掉,使得当前结点的入度为0 ,但是当前结点入度是不可能为0的。所以不可能加入队列。

这就类似于,难度1-8关 必须一关一关通过不能跳关递增 ,题目要求通过第八关才能通过第四关,但是第八关在第四关的后面,只有通过第四关才能通过第八关。

如果出现这样的情况,那么一定存在结点没有被遍历

注:最后如果发现关系不明确也要继续做,因为可能出现关系不明确,且关系冲突的情况,这样输出冲突。

image.png

#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;
}