CF1132D Gourmet choice 并查集+拓扑排序

原题链接:D. Gourmet choice

题目大意

给出一个(n*m)的二维字符数组,其中(s_{i,j})表示(a_i)(b_j)的关系,如相等,小于或大于.求是否存在一组({a},{b})使得能满足条件,如果能则输出最大值最小的解,如果不能输出无解.

数据范围:

(1 leq n,m leq 1000)

思路

显然所有相等的元素,可以先合并到一起,这一步可以并查集来做,不过需要拓展域:([1,n])的元素,维护的是(a_i)属于哪个集合,([n+1,m])的元素维护的是(b_j)属于哪个集合.合并完之后考虑小于和大于的偏序关系,很显然是让相同的元素的集合看做是一整个块再去和别的集合连边,那么为了保证最大值最小,如果关系是(a_i<b_j)那么就让(a_i)所在的集合连一条有向边到(b_j)所在的集合,那么如果(a_i)(b_j)本来就是一个集合的,这里又出现了一个偏序关系,则显然是无解的.连完边之后,整个图应该是一个有向图,而且一旦存在环就意味着产生了一个形如(a_i>b_j>...>a_i)的结构,显然也是无解的.那么把出现有环的情况再删除之后,整个图就是一个有向无环图了,对之直接拓扑排序即可.

检查是否存在环这一步也应该放在拓扑排序里,简单的搜索被挂了一个TLE.

其次,拓扑排序的点是并查集合并之后的"代表元素",那么就不能用原有给定的下标了,所以代码实现的时候先把所有的入度初始化成-1,而只有当某个代表元素,也就是新的图上的点的编号被拿出来的时候才会给这个(deg[i])赋值成(0).之后拓扑排序判环的时候也不能判断出队的元素是否是(n+m)个而是新的点数.

当然,这个题还有个坑点,假如说有解的话,所有的答案最小值都是(1),如果不对所有(ans)一开始就赋值的话,而是等到在拓扑排序的时候再对入度为(0)的点进行赋值为(1)的操作,那么假如整个图上就没有任何的偏序关系,他也是可能有解的,但是此时的答案就会输出成(0)而正确答案是(1),因为此时拓扑排序都没启动,没有正确的对所有起点赋初值.当然解决办法就是一开始就直接对所有点赋初值.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	

const int N = 1005;
char s[N][N];
int fa[2 * N],st[N * 2],ans[N * 2],deg[N * 2];
int n,m,in_path[N * 2];
vector<int> E[N * 2];

int find(int x)
{
	if(fa[x] == x)	return x;
	return fa[x] = find(fa[x]);
}

int topo()
{
	queue<int> q;
	forn(i,1,n + m)	if(deg[i] == 0)	q.push(i);
	
	int in_times = 0;
	while(!q.empty())
	{
		++in_times;
		int u = q.front();q.pop();
		for(auto& v : E[u])
		{
			if(--deg[v] == 0)	
				q.push(v),ans[v] = ans[u] + 1;
		}
	}
	return in_times;
}

int main()
{
	scanf("%d%d",&n,&m);
	forn(i,1,n)	scanf("%s",s[i] + 1);
	forn(i,1,n + m)	fa[i] = i,ans[i] = 1;
	
	forn(i,1,n)	forn(j,1,m)
		if(s[i][j] == '=')
		{
			int x = find(i),y = find(j + n);
			if(x == y)	continue;
			fa[x] = y;
		}
	
	
	memset(deg,-1,sizeof deg);
	int fuck = 0;
	forn(i,1,n)	forn(j,1,m)
	{
		if(s[i][j] == '=')	continue;
		int x = find(i),y = find(j + n);
		if(deg[x] == -1)	deg[x] = 0,++fuck;
		if(deg[y] == -1)	deg[y] = 0,++fuck;
		if(x == y)	return puts("No"),0;
		if(s[i][j] == '<')	E[x].push_back(y),++deg[y];
		else				E[y].push_back(x),++deg[x];
	}
	
	
	if(topo() != fuck)	return puts("No"),0;
	
	puts("Yes");
	forn(i,1,n)	printf("%d ",ans[find(i)]);puts("");
	forn(i,1,m)	printf("%d ",ans[find(i + n)]);
	
    return 0;
}
原文地址:https://www.cnblogs.com/HotPants/p/14296481.html