原文链接:https://www.cnblogs.com/Time25/p/19824393.html

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include<bits/stdc++.h>
using namespace std;
//"O campeão tem nome, e se chama Charles Oliveira!"
#define int long long
#define endl '\n'
#define ep emplace
#define pob
#define ll long long
#define pb push_back
#define pof pop_front
#define pob pop_back
#define all(a) a.begin(),a.end()
#define rall(a) a.rbegin(),a.rend()
#define mod 998244353
#define MOD 1000000007
#define N 200010
#define INF 1e18

using ld = long double;
using ui = unsigned;
using ull = unsigned long long;
using i128 = __int128;

int check_topo(int n, const vector<vector<int>>& adj, string& res) {
vector<int> in_degree(n, 0);//这里的in_degree里,开始全都初始化为0了
//所以就算是语句中只出现了 A和B,比如说A<B的化,那么A和C都会被输入到q中去
for (int u = 0; u < n; u++) {
for (int v : adj[u]) in_degree[v]++;
}
//每次都要从头统计一次,防止新的命令出现产生了环
queue<int> q;
for (int i = 0; i < n; i++) {
if (in_degree[i] == 0) q.push(i);
}

bool is_undetermined = false;
res = "";
int count = 0;

while (!q.empty()) {
// 关键点:如果队列中超过一个元素,说明有多个起点,顺序不唯一
//比如是3个点ABC,但是只有一条命令A<B,这个时候我们的队列中就只有A和C,这个时候的size就是大于1的
if (q.size() > 1) is_undetermined = true;

int u = q.front();
q.pop();
count++;
res += (char)('A' + u);

for (int v : adj[u]) {
if (--in_degree[v] == 0) {
q.push(v);
}
}
}

// 如果处理的节点数少于 n,说明图中存在环
//不管是只执行了一条语句还是执行了好多条语句,只要没有环的出现,就是count==n的
if (count < n) return 0;
// 如果虽然处理完了 n 个点,但中间出现过分支
if (is_undetermined) return 2;
// 否则,唯一序
return 1;
}

void solve() {
int n, m;
while (cin >> n >> m && (n || m)) {
vector<vector<int>> adj(n);
bool finished = false;
string res;

for (int i = 1; i <= m; i++) {
string s;
cin >> s;
if (finished) continue;

int u = s[0] - 'A';
int v = s[2] - 'A';
adj[u].push_back(v);

int status = check_topo(n, adj, res);

if (status == 0) {
printf("Inconsistency found after %d relations.\n", i);
finished = true;
} else if (status == 1) {
cout << "Sorted sequence determined after " << i << " relations: " << res << "." << endl;
finished = true;
}
}

if (!finished) {
printf("Sorted sequence cannot be determined.\n");
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t=1;
//cin>>t;
while(t--)solve();
}

分析

简单来说就是,给你几个字符串,类似这样的

1
2
3
4
5
6
7
8
4 6
A<B
A<C
B<C
C<D
B<D
A<B


要你分析,这个构成的这个关系是不是合法的/违法的/不明确的
如果有着合理的A
1
2
26 1
A<Z

是这样要有26个字母之间的关系,但是命令行不够的话,那就是不明确的
很容易想到拓扑排序

topo函数

因为我们在每一个字符串被传入进来的时候,都要检查一次,所以我们的拓扑函数传入的参数是要带我们建表的数组的
int check_topo(int n, const vector>& adj, string& res),就是这个adj

1
2
3
for (int u = 0; u < n; u++) {
for (int v : adj[u]) in_degree[v]++;
}

每次我们都要统计一下这个表中现在的情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
queue<int> q;
for (int i = 0; i < n; i++) {
if (in_degree[i] == 0) q.push(i);
}

bool is_undetermined = false;
res = "";
int count = 0;

while (!q.empty()) {
// 关键点:如果队列中超过一个元素,说明有多个起点,顺序不唯一
//比如是3个点ABC,但是只有一条命令A<B,这个时候我们的队列中就只有A和C,这个时候的size就是大于1的
if (q.size() > 1) is_undetermined = true;

int u = q.front();
q.pop();
count++;
res += (char)('A' + u);

for (int v : adj[u]) {
if (--in_degree[v] == 0) {
q.push(v);
}
}
}

这个就是经典的topo排序的板子了,中间加了一个这个
1
2
3
bool is_undetermined = false;
res = "";
int count = 0;

1.这个is_undetermined,是用来判断这个是不是不明确的情况
2.res是用来记录结果字符串的,因为题目说,如果这个是合法的话,就要输出这个合法的序列是怎么样的
3.count是用来记录这个拓扑排序最后有几个元素的,如果元素个数小于n个说明成环了,也就是不合法的情况出现了

具体细节处理

疑惑分析1

1
if (count < n) return 0;

当命令没有完全读完的时候,会不会出现这个情况呢?答案是不会的,假设命令是完整的,就是不会出现像下面这种情况
1
2
26 1
A<Z

那么,cnt就永远是n,为什么呢,假设我们是这里例子
1
2
3
4
5
6
7
4 6
A<B
A<C
B<C
C<D
B<D
A<B

当我们只读了一条语句的时候:vector> adj(n);我们刚开始是初始化所有是0,也就是说,没有读入的那些点就是0,也就是会被加入到队列中去的,但是这个时候对列中的size就会大于1,
所以是return 2;

// TRAINING LOG · 第 44 / 76 篇训练记录 · 成文于 2026.04.06 · 上午 10:29