HDU1811 Rank of Tetris——拓扑排序(BFS+并查集缩点)

点这里


题意: n个人个m个约束条件,每行约束条件都有“<>=”符号,“=”表示两人同级,两个人之间编号大的在前。按照已知条件如果排名唯一,则输出OK;排名不唯一则输出UNCERTAIN;约束条件之间矛盾则输出CONFLICT。
题解:

  • 缩点(并查集)。 不要想 的太复杂 (我就是一直在考虑“=”怎么处理,一直没敢下手) 其实所有=联系的点,合并成一个点处理即可,他们内部的排名肯定是能够确认的,题目不要求输出排名,那就不用在意,看成一个点处理就行。这步处理,则是采用并查集的方式。
  • CONFLICT。 什么样情况下会矛盾,其实应该说条件矛盾会造成什么后果。一种情况是同时出现a = ba > b,这个很容易判断,一开始读入的时候,就可以将所有“=”的两个点合并,之后再遍历每一条边,检查是否有冲突。第二种情况 是出现a > ba < b的环,造成的后果是拓扑排序的BFS中,出现一些点的入度不为零,无法入队,这种情况只要跑完一遍BFS检查是否还要入度不为零的点即可。
  • UNCERTAIN。 想一下什么条件可能造成最后排名不唯一,可一句样例1 > 0, 1 > 2, 2 < 1,如果去掉重边,观察0、1、2三个点的入度分别为0、1、1。也就是说再遍历点1的边时,0和2的入度会同时减为0,并且入队。即如果在BFS排序过程中,队列中出现了两个及以上的元素,说明最终排名可能不唯一。

过程中犯的错:

  • sum可能为负。 虽然进行了缩点,,但实际上的点数没有改变。(sum的初始值为n,程序中每出现一次“=”,sum就会减一;每当一个点入队,sum就会减一),进行过缩点操作之后,我的程序中认可的点可能只有x个,但是出现的点可能一共会有n个,n如果>x,sum会出现负数。所以检查是否所有(认可的)点都入队,应判断最后的sum>0?而不是判断sum==0?
  • BFS函数必须完整执行。 一开始我一旦找到队列中包含两个及以上的元素,说明最后排名可能不唯一的时候,我写了一个return。实际上如果这个函数不执行完的话,还会影响sum的结果,而sum的结果先影响了是否输出UNCERTAIN。因此这里不能return

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;

int n, m, sum;
int deg[N], s[N];
bool flag, sign;
vector<int> v[N];
struct edge{	int x, y; char c;}e[2 * N];
int find(int x){	return s[x] == x? x: s[x] = find(s[x]);}
void un(int a, int b){	int sa = find(a), sb = find(b);	if(sa != sb) s[sa] = sb;}
void check(){
	for(int i = 0; i < m; i++){
		if(e[i].c == '=')	continue;
		int a = find(e[i].x), b = find(e[i].y);
		if(a == b){	flag = false; return;}
		if(e[i].c == '<'){
			deg[a]++;
			v[b].push_back(a);
		}else{
			deg[b]++;
			v[a].push_back(b);
		}
	}
}
void BFS(){
	queue<int> q;
	for(int i = 0; i < n; i++){
		int a = find(i);
		if(!deg[a] && s[a] == i)	q.push(a);
	}
	while(!q.empty()){
		int a = q.front(); q.pop();	sum--;
		if(!q.empty())	sign = false;	//不能return 
		for(int i = 0; i < v[a].size(); i++){
			int b = v[a][i];
			deg[b]--;
			if(!deg[b])	q.push(b);
		}
	}
}
int main(){
	while(~scanf("%d%d", &n, &m)){
		sum = n; flag = sign = true;
		for(int i = 0; i < n; i++)	deg[i] = 0,	s[i] = i, v[i].clear();	//init();
		for(int i = 0; i < m; i++){
			scanf("%d %c %d", &e[i].x, &e[i].c, &e[i].y);
			if(e[i].c == '=')	un(e[i].x, e[i].y), sum--;
		}
		check();
		BFS();
		if(sum > 0 || !flag)printf("CONFLICT\n");	//不能 sum != 0 
		else if(!sign)		printf("UNCERTAIN\n");
		else				printf("OK\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值