题目地址:
https://www.acwing.com/problem/content/345/
给定 n n n个变量和 m m m个不等式。其中 n n n小于等于 26 26 26,变量分别用前 n n n的大写英文字母表示。不等式之间具有传递性,即若 A > B A>B A>B且 B > C B>C B>C,则 A > C A>C A>C。请从前往后遍历每对关系,每次遍历时判断:如果能够确定全部关系且无矛盾,则结束循环,输出确定的次序;如果发生矛盾,则结束循环,输出有矛盾;如果循环结束时没有发生上述两种情况,则输出无定解。
输入格式:
输入包含多组测试数据。每组测试数据,第一行包含两个整数
n
n
n和
m
m
m。接下来
m
m
m行,每行包含一个不等式,不等式全部为小于关系。当输入一行
0
0
0时,表示输入终止。
输出格式:
每组数据输出一个占一行的结果。结果可能为下列三种之一:如果可以确定两两之间的关系,则输出Sorted sequence determined after t relations: yyy...y.
,其中t
指迭代次数,yyy...y
是指升序排列的所有变量。如果有矛盾,则输出:Inconsistency found after t relations.
,其中t
指迭代次数。如果没有矛盾,且不能确定两两之间的关系,则输出Sorted sequence cannot be determined.
。
数据范围:
2
≤
n
≤
26
2≤n≤26
2≤n≤26,变量只可能为大写字母
A
∼
Z
A∼Z
A∼Z。
本质上是在求传递闭包,传递闭包是指,对于一个有向图,如果存在 a → b a\to b a→b的路径,那么就一定存在 a a a到 b b b的一条边。在每加入一条边的时候,就求一下是否存在合法的传递闭包,即不能出现 x < x x<x x<x,并且对任意 x , y x,y x,y,都有 x < y x<y x<y或 y < x y<x y<x。如果所有边加上去都不能导致后者成立,则说明不能决定。求传递闭包可以用类似Floyd算法的办法,设 d [ x ] [ y ] = 1 d[x][y]=1 d[x][y]=1代表已经知道 x < y x<y x<y, d [ x ] [ y ] = 0 d[x][y]=0 d[x][y]=0代表 x < y x<y x<y还未决定。加上一条边 ( a , b ) (a,b) (a,b)的时候,就知道了 d [ a ] [ b ] = 1 d[a][b]=1 d[a][b]=1,此外对所有 x → a x\to a x→a, d [ x ] [ b ] = 1 d[x][b]=1 d[x][b]=1,对所有 b → y b\to y b→y, d [ a ] [ y ] = 1 d[a][y]=1 d[a][y]=1,同时还要更新 d [ x ] [ y ] = 1 d[x][y]=1 d[x][y]=1。每次加上一条边的时候,就判断一下是否已经存在合法的传递闭包了,如果存在,就逐次求未标记的点中的最小值,并每次把当前最小值标记下。这些最小值的序列就是一个全序序列。
那么是否存在一个合法的传递闭包,就一定存在一个全序序列呢(也就是上面的步骤是否每次都能找到最小值呢)。答案是肯定的。否则的话,就可以找到一个无穷序列,使得 . . . > x k > x k − 1 > . . . > x 2 > x 1 ...>x_k>x_{k-1}>...>x_2>x_1 ...>xk>xk−1>...>x2>x1,从而有环,就能推出存在某个 x x x使得 d [ x ] [ x ] = 1 d[x][x]=1 d[x][x]=1,这就与传递闭包合法矛盾了。
代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 26;
int n, m;
int d[N][N];
bool st[N];
int check() {
// 首先不能有x < x
for (int i = 0; i < n; i++)
if (d[i][i]) return -1;
// 其次不能存在两个不可比较的点
for (int i = 0; i < n; i++)
for (int j = 0; j < i; j++)
if (!d[i][j] && !d[j][i])
return 0;
return 1;
}
char get() {
// 找到一个极小元。由于在传递闭包合法的情况下存在全序,所以极小元也就是最小元
for (int i = 0; i < n; i++) {
if (st[i]) continue;
bool found = false;
for (int j = 0; j < n; j++)
if (!st[j] && d[j][i]) {
found = true;
break;
}
if (!found) {
st[i] = true;
return 'A' + i;
}
}
return 0;
}
int main() {
while (cin >> n >> m, n || m) {
int type = 0, t;
memset(d, 0, sizeof d);
for (int i = 1; i <= m; i++) {
char str[5];
cin >> str;
int a = str[0] - 'A', b = str[2] - 'A';
if (!type) {
// 读入一条边,并更新传递闭包
d[a][b] = 1;
for (int x = 0; x < n; x++) {
if (d[x][a]) d[x][b] = 1;
if (d[b][x]) d[a][x] = 1;
for (int y = 0; y < n; y++)
if (d[x][a] & d[b][y])
d[x][y] = 1;
}
type = check();
// 记录一下产生矛盾或找到合法传递闭包的轮数,
// 注意此时不能跳出循环,而要把接下来的边都读下来
if (type) t = i;
}
}
if (type == -1) printf("Inconsistency found after %d relations.\n", t);
else if (type == 0) printf("Sorted sequence cannot be determined.\n");
else {
memset(st, 0, sizeof st);
printf("Sorted sequence determined after %d relations: ", t);
for (int i = 0; i < n; i++)
printf("%c", get());
printf(".\n");
}
}
return 0;
}
时间复杂度 O ( m n 2 ) O(mn^2) O(mn2)。