题目链接:Problem - 1811 (hdu.edu.cn)
这道题目综合考察了拓扑序列和并查集,刚看到这道题目时,我的想法是对于rating不同的人我们直接按照给定的rating值大小进行连边,而对于rating值相同的人我们根据其人品值进行连边,但这样的想法是错误的,比如给出 3>4,3=5,这样就会有4和5分别向3连一条边,可以确定的是3大于4以及3大于5,但是并未确定4与5的关系,由于5=3,故应该有5大于4,所以我们利用之前的想法并不能很好地处理这种问题,那我们应该怎么做呢?对于rating值相等的元素一定存在唯一的大小关系,所以我们只需要处理rating值不相同的元素之间的关系即可,所以我们可以把rating值相同的元素放入一个集合,在处理之后的关系时我们只需要用集合的代表元素进行判断即可,其余的就是一个基本的拓扑排序问题了。
下面是代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=5e5+10;
int n,m,cnt;
int fu[N],h[N],e[N],ne[N],idx,ru[N];
int x[N],y[N],uflag,cflag;
char op[N];
void init()
{
memset(h,-1,sizeof h);
memset(ru,0,sizeof ru);
for(int i=0;i<n;i++) fu[i]=i;
idx=0;cnt=0;uflag=0;cflag=0;
}
int find(int x)
{
if(x!=fu[x]) fu[x]=find(fu[x]);
return fu[x];
}
void add(int x,int y)
{
e[idx]=y;
ne[idx]=h[x];
h[x]=idx++;
}
void topsort()
{
queue<int> q;
for(int i=0;i<n;i++)
if(!ru[i]&&find(i)==i) cnt++,q.push(i);//要保证一个集合最多只能有一个元素入队,即代表元素
while(q.size())
{
if(q.size()>1) uflag=1;
int begin=q.front();
q.pop();
if(cflag) continue;
for(int i=h[begin];i!=-1;i=ne[i])
{
int j=e[i];
ru[j]--;
if(!ru[j])
{
q.push(j);
cnt++;
}
}
}
if(cnt<n) cflag=1;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(int i=1;i<=m;i++)
{
cin>>x[i]>>op[i]>>y[i];
//先把rating相同的人放入同一个集合,之后进行操作时只需用集合的代表元素进行操作
if(op[i]=='=')
{
int u=find(x[i]),v=find(y[i]);
if(u!=v)
{
cnt++;
fu[u]=v;
}
}
}
for(int i=1;i<=m;i++)
{
if(op[i]=='=') continue;
int u=find(x[i]),v=find(y[i]);//用集合的代表元素进行操作
if(op[i]=='<')
{
add(u,v);
ru[v]++;
}
else
{
add(v,u);
ru[u]++;
}
}
topsort();
if(cflag) puts("CONFLICT");
else if(uflag) puts("UNCERTAIN");
else puts("OK");
}
return 0;
}