题意很明了,但是我不知道是不是有人和我一样,一开始在纠结一个愚蠢的问题:
0 > 1, 0 > 2; 应该是能确定的,因为0 大于1,0大于2,那么1和2又可以按人品排名,应该是OK呀。。。。好吧,实在是智商捉急..答案是不确定的,因为按人品排名,我们只能是在 1 = 2 的情况下,没有说明 = ,那么怎么可以认为等级一样呢?
下面开始详细题解分析:
最开始做的时候,是在图论500题上看到的,分类是 拓扑+并查集,两个基本知识点都不难,我就上了。。谁知道。。。首先要想到,然后就是细心,我的处理方式是这样的:
1,先把等级一样的 ,也就是有‘=’ 的两个点并在一个并查集里面,也就是说,等级相同的,我们就看做一个点,用祖先root表示,其他的先不处理,用数组 a[i] ch[i] b[i] 保存起来
2,然后我们再for(0 ~ M) 条边,遍历一次,用数组邻接表建有向图,记录入度,同时记录新图当中的点数node_num(这里等级相同的在一个集合里面看做一个点root)
3,然后就开始 拓扑排序,那么这里需要记录的情况比较杂乱,要细心
4,首先将入度为 0 的点入队,(这里用队列+邻接表来拓扑也是第一次,每次出队一个 u,我们对其进行拓扑,记录拓扑的点数 topo_num ++;然后用邻接表遍历邻接点 v--也就相当于删边操作,遍历过程中,邻接点 v 入度--,如果减完之后为0,那么入队,同时,记录一个节点删除会带来的入度为0的点数 num ++,如果num > 1,那么这里排名就不确定啦),那么上面说的就是用队列+邻接表处理拓扑排序的一般方法,我们在这个题目中需要添加一点点东西,在对于出队的点,用邻接表遍历邻接点的时候,我们就把u 和 v 并在一个并查集里面,这里是为了后面判断拓扑完后,所有的点是不是在一个集合里面
OK 基本的方法就在上面啦,那么这里主要情况比较复杂就在于什么时候我们是冲突,什么时候不确定,什么时候是OK,这里我这样判断的:
OK 上马:
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
#define MAX 10005
int N,M;
int degree[MAX];//记录入度
int father[MAX];//并查集
struct Edge
{
int to,next;
} edge[MAX*2];
int head[MAX],tol;
void add(int u,int v)
{
edge[tol].to = v;
edge[tol].next = head[u];
head[u] = tol++;
}
int find(int x)
{
if(x != father[x]){
father[x] = find(father[x]);
}
return father[x];
}
char ch[MAX];
int a[MAX],b[MAX];
void initMap()
{
for(int i = 0; i < N; i ++) father[i] = i;
for(int i = 0; i < M; i ++)
{
scanf("%d %c %d",&a[i],&ch[i],&b[i]);
if(ch[i] == '=') //同一级别的放在一个集合中
{
int x = find(a[i]);
int y = find(b[i]);
father[x] = find(y);
}
}
//第二步来建图
tol = 0;
memset(head,-1,sizeof(head));
memset(degree,0,sizeof(degree));
for(int i = 0; i < M; i ++)
{
int x = find(a[i]),y = find(b[i]);
if(ch[i] == '>'){
add(x,y); degree[y] ++;
}
else if(ch[i] == '<'){
add(y,x); degree[x] ++;
}
}
}
int topo()
{
int node_num = 0;//新建图的点数
queue<int>q;
for(int i = 0; i < N; i ++)
{
if(father[i] == i){
node_num ++;
if(degree[i] == 0) q.push(i);
}
}
int topo_num = 0;//经过topo的点数
bool unsure = false;//是不是出现不确定情况
while(!q.empty())
{
int num = 0;//这一轮过程中产生入度为0的点数
int u = q.front();
q.pop();
topo_num++;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
degree[v] --;
father[find(u)] = find(v); //并入
if(!degree[v]){
num++; q.push(v);
}
}
if(num > 1) unsure = true;//就已经说明不确定啦
}
bool inAset = true;
for(int i = 0; i < N-1; i ++){
if(find(i) != find(i+1)){ //两两之间在一个集合
inAset = false;
}
}//下面的判断结合上图的分析
if(topo_num != node_num) return 1;
else{
if(inAset){
if(unsure) return 2;
else return 0;
}else return 2;
}
}
int main()
{
while(cin >> N >> M)
{
initMap();
int ans = topo();
if(ans == 1) cout<<"CONFLICT"<<endl;
else if(ans == 2) cout << "UNCERTAIN" <<endl;
else cout << "OK" <<endl;
}
return 0;
}
OK 个人愚昧观点,欢迎指正与讨论