并查集+拓扑排序。给定点之间的关系,让你判断是否可以确定整个严格排序序列,若不能确定,还要判断是因为信息不完全 还是 信息冲突(优先)。
点之间相等的话总是可以根据RP
排序,所以如果某些点相等的话,他们内部的排序就唯一确定了,而且他们在总排序上是连续的一段。
所以相等关系可以用并查集处理,看成一个"连通分量",采取“缩点”的思想,可以由每个根来代表。
信息冲突分为两种:有环和非法(A = B, A > B
或者A > A
,可以表示为连通分量内不能再有大小关系)。共三种情况。
- 先用并查集判断"连通分量"内的非法,要全部关系输入之后再对每一个大于小于关系进行判断。
这一步其实是判断每个圈子内部的冲突。这步判断完了,就相当于跳出圈子了,就转化为一个正常的有向图了,不再有什么相等关系了。每个圈子缩为一个点,以这个点来表达这个圈子内到其他圈子的关系。
另外,在这一步之后,其实可以这么转化:该图中必须可以找到一条从上而下的链(这条链依次连接每个结点),在此之外,可以有跨越边,可以有重边,但是不能有任何反向边。
- 判环。这里判环是有向图,所以用拓扑排序。“环”就对应反向边。
- 判断信息不完全(假设现在已经无环的前提下,下述判断才成立)。判断是否 信息不完全 等价于 判断是否 有一条连接每个结点的链 等价于 判断是否 拓扑排序的每次循环都只
push
一个结点(包括初始化)、直至链尾(push
零个结点)。
注意的是,第二点覆盖第三点,而第二点是最后才判断,第三点是在中途判断的。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 10001;
int N, M;
int pre[MAXN];
struct Edge
{
int from, to;
};
vector<Edge> ve;
vector<int> v[MAXN];
int indegree[MAXN];
int flag;
void init()
{
memset(pre, -1, sizeof pre);
ve.clear();
for (int i = 0; i < N; i++)
{
v[i].clear();
indegree[i] = 0;
}
flag = 0;
}
int f(int x)
{
if (pre[x] < 0) return x;
return pre[x] = f(pre[x]);
}
void u(int n1, int n2)
{
int f1 = f(n1);
int f2 = f(n2);
if (f1 != f2)
{
if (pre[f1] <= pre[f2])
{
pre[f1] += pre[f2];
pre[f2] = f1;
}
else
{
pre[f2] += pre[f1];
pre[f1] = f2;
}
}
}
void topological()
{
int n = 0; // 拓扑排序统计的结点总数
int num = 0; // 出队总数
queue<int> q;
for (int i = 0; i < N; i++)
{
if (pre[i] < 0) // 只统计每个连通分量的根
{
n++;
if (indegree[i] == 0)
q.push(i);
}
}
if (q.empty())
{
flag = 1; // 刚开始就找不到入度为0的点,说明有环
return;
}
for (; !q.empty();)
{
if (q.size() > 1) flag = 2; // 只要有一个时刻队列中同时存在一个点以上,就"至少"说明信息不完全
int t = q.front();
q.pop();
num++;
for (int i = 0; i < v[t].size(); i++)
{
indegree[v[t][i]]--;
if (indegree[v[t][i]] == 0)
q.push(v[t][i]);
}
}
if (n != num) flag = 1; // 信息冲突可以"覆盖"信息不完全
}
int main()
{
int a, b;
char c;
for (; ~scanf("%d%d", &N, &M);)
{
init();
for (int i = 0; i < M; i++)
{
scanf("%d %c %d", &a, &c, &b); // 格式自动吸收空格
if (c == '=') u(a, b);
else if (c == '>') ve.push_back({ a,b });
else if (c == '<') ve.push_back({ b,a });
}
for (int i = 0; i < ve.size(); i++)
{
int n1 = f(ve[i].from);
int n2 = f(ve[i].to);
if (n1 == n2)
{
flag = 1;
break;
}
v[n1].push_back(n2);
indegree[n2]++;
}
if (flag == 1)
printf("CONFLICT\n");
else
{
topological();
if (flag == 1) printf("CONFLICT\n");
else if (flag == 2) printf("UNCERTAIN\n");
else printf("OK\n");
}
}
return 0;
}