根据题单的提示,这是一道拓补排序+并查集的题目,然而我并不会拓补排序......所以这里先写一下拓补排序相关的东西吧......
首先,拓补排序是对一系列有先后顺序的“活动”的排序。何为“有先后顺序的‘活动’”?举个例子,假如现在某人要穿衣服,那他必须要先穿内衣再穿中间的毛衣,最后再穿外套。拓补排序就是使用有向无环图(DAG)来对这些有先后顺序的活动进行排序,得到一个可行的顺序。对于同一系列的活动,可能有多于一种顺序可以使他们满足拓补排序。这跟穿衣服时可以先穿裤子再穿毛衣然后再穿外套,也可以先穿毛衣再穿裤子然后穿外套是一样的。
拓补排序的实现:先找出入度为0的点,然后将这个点以及与这个点相连的边删除,并将与这个点直接相连的点的入度都减一。再找出当前入度为0的点,重复上述过程.......当所有点的入度都为0时,说明所有的点都已经被安排了一个拓补序,拓补排序完成。如果不能使所有点的入度都为0,则可以判断当前这个图不是DAG。因此,拓补排序还可以用来判断一个有向图是否有环
具体到这道题。思路是,对于rating相等的两个点,比如说a和b,因为它们之间的序号一定不一样,所以两者之间一定能够连接一条有向边。可是如果接下来又出现了一个b = c呢?那b和c之间也要连一条有向边。不仅如此,a和c之间也要连一条有向边。所以如果出现了a = b = c = d = e = ...的情况,这个图就会很复杂。所以我们可以用并查集将那些rating相等的点都连接起来,然后将他们的根节点作为他们的代表元素,即将这些点看成是一个点。然后再用拓补排序判一下环即可。
需要注意的细节是,如果之前已经出现了a = b,往后可能会出现a > b或者a < b,这是要判断为CONFLICT
代码如下:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <cctype>
#include <string>
#include <queue>
#define debug printf("debug\n")
#define mst(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=1e4+5;
int n,m;
//n个点,m条边
//将'='的点用并查集聚合为一个点
int pre[2*maxn];
int head[2*maxn];
int deg[2*maxn]; //记录每个点的入度
int tot;
struct edge{
int to;
int next;
};
edge edges[2*maxn];
void init()
{
tot=0;
for(int i=0;i<n;i++)
pre[i]=i;
mst(head,-1);mst(deg,0);
}
void add_edge(int u,int v)
{
edges[tot].to=v;
edges[tot].next=head[u];
head[u]=tot++;
deg[v]++;
}
int findr(int x)
{
if(x==pre[x])
return x;
return pre[x]=findr(pre[x]);
}
int topo()
{
queue<int> que;
int root_cnt=0;
int in_que_cnt=0;
bool flag=1;
for(int i=0;i<n;i++)
{
if(pre[i]==i){
root_cnt++;
if(deg[i]==0)
que.push(i);
}
}
while(!que.empty())
{
if(que.size()>1)
flag=0;
in_que_cnt++;
int fro=que.front();
que.pop();
for(int i=head[fro];i!=-1;i=edges[i].next)
{
int tmp=findr(edges[i].to);
deg[tmp]--;
if(deg[tmp]==0)
que.push(tmp); //为什么这里不需要将入队次数++?
}
}
if(in_que_cnt<root_cnt)
return 1; //冲突(有环)
else if(!flag)
return -1; //不可确定关系
else
return 0; //ok
}
int a[maxn],b[maxn],ch[maxn];
int main()
{
while(scanf("%d %d",&n,&m)!=EOF)
{
init();
getchar();
for(int i=0;i<m;i++)
{
scanf("%d %c %d",&a[i],&ch[i],&b[i]);
getchar();
if(ch[i]=='='){
int fa=findr(a[i]);
int fb=findr(b[i]);
if(fa!=fb) //若rating相等,则合并,因为a和b的序号
//一定不一致,绝对可以比出大小
pre[fa]=fb;
}
}
int ans=0;
for(int i=0;i<m;i++)
{
int fa=findr(a[i]);
int fb=findr(b[i]);
if(ch[i]!='='&&fa==fb){
ans=1;
break;
}
if(ch[i]=='<'){ //a < b
add_edge(findr(b[i]),findr(a[i]));
}else if(ch[i]=='>'){
add_edge(findr(a[i]),findr(b[i]));
}
}
if(ans==0)
ans=topo();
if(ans==1)
cout<<"CONFLICT"<<endl;
else if(ans==-1)
cout<<"UNCERTAIN"<<endl;
else
cout<<"OK"<<endl;
}
return 0;
}