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