AcWing 343. 排序
问题描述
分析
题目给 X < Y 的大小关系,要求按输入顺序逐条加入关系,并在出现以下三种情况的最早时刻停下来:
- 关系之间出现矛盾(例如能同时推出
和 ); - 能唯一确定所有
个变量的总顺序; - 全部
条关系处理完仍无法确定顺序。
关键建模:把每条关系
- 矛盾
图中出现环 存在 使 且 ; - 能唯一确定顺序
任意两个不同变量都可比较,即对一切 有 或 (只要有一对不可比,就至少存在两种合法的线性顺序); - 否则说明当前信息不足,需要继续读入下一条关系。
一旦确定了唯一顺序,输出方式为:不断从未标记的变量中选出"没有未标记前驱"的最小者(即不存在
思路一:Floyd 传递闭包(朴素)
每加入一条关系
然后用 check() 判定当前状态:先查是否有环(矛盾,返回
正确性:Floyd 求出的是可达性闭包,
复杂度:
思路二:增量更新(优化)
新加入的边
- 若
,则 (路径 ); - 若
,则 (路径 ); - 对任意
,若 且 ,则 。
因为
参考代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 26;
int d[N][N], st[N];
int n, m;
void floyd() {
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
d[i][j] |= d[i][k] && d[k][j];
}
}
}
}
int check() {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (d[i][j] == 1 && d[j][i] == 1) return 2;
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (d[i][j] == 0 && d[j][i] == 0) return 0;
}
}
return 1;
}
char get_min() {
for (int i = 0; i < n; i++) {
if (!st[i]) {
bool flag = true;
for (int j = 0; j < n; j++) {
if (!st[j] && d[j][i]) {
flag = false;
break;
}
}
if (flag) {
st[i] = true;
return 'A' + i;
}
}
}
}
int main() {
while (cin >> n >> m, n || m) {
memset(d, 0, sizeof d);
// t: 0 不能确定关系 1 c 次之后能确定关系 2 c 次之后有矛盾
int t = 0, c;
for (int i = 1; i <= m; i++) {
char s[5]; cin >> s;
int a = s[0] - 'A', b = s[2] - 'A';
if (!t) {
d[a][b] = 1;
floyd();
t = check();
if (t) c = i;
}
}
if (!t) puts("Sorted sequence cannot be determined.");
else if (t == 2) printf("Inconsistency found after %d relations.\n", c);
else {
printf("Sorted sequence determined after %d relations: ", c);
memset(st, 0, sizeof st);
for (int i = 0; i < n; i++) printf("%c", get_min());
puts(".");
}
}
return 0;
}优化
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 26;
int d[N][N], st[N];
int n, m;
int check() {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (d[i][j] == 1 && d[j][i] == 1) return 2;
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (d[i][j] == 0 && d[j][i] == 0) return 0;
}
}
return 1;
}
char get_min() {
for (int i = 0; i < n; i++) {
if (!st[i]) {
bool flag = true;
for (int j = 0; j < n; j++) {
if (!st[j] && d[j][i]) {
flag = false;
break;
}
}
if (flag) {
st[i] = true;
return 'A' + i;
}
}
}
}
int main() {
while (cin >> n >> m, n || m) {
memset(d, 0, sizeof d);
// t: 0 不能确定关系 1 c 次之后能确定关系 2 c 次之后有矛盾
int t = 0, c;
for (int i = 1; i <= m; i++) {
char s[5]; cin >> s;
int a = s[0] - 'A', b = s[2] - 'A';
if (!t) {
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;
}
}
t = check();
if (t) c = i;
}
}
if (!t) puts("Sorted sequence cannot be determined.");
else if (t == 2) printf("Inconsistency found after %d relations.\n", c);
else {
printf("Sorted sequence determined after %d relations: ", c);
memset(st, 0, sizeof st);
for (int i = 0; i < n; i++) printf("%c", get_min());
puts(".");
}
}
return 0;
}