[ECNA 2001] 排序

GitHub跳转原题关系图返回列表

每加入一条大小关系就重跑拓扑排序,用队列分支数判断唯一序列并用处理点数判断矛盾。

OJ: luogu

题目 ID: P1347

难度:普及/提高-

标签:拓扑排序DAG唯一性python

日期: 2026-07-16 18:42

题意

依次给出 A<B 形式的关系。求最早在第几条关系后能唯一确定全部顺序,或最早发现矛盾;若所有关系处理完仍不能确定,则输出无法确定。

思路

A<B 建成有向边 A -> B。每加入一条新边就对当前图做一次 Kahn 拓扑排序:

  • 最终处理点数小于 n,说明有环,当前关系产生矛盾;
  • 每一步零入度队列都恰好只有一个点,说明每个位置都没有第二种选择,拓扑序唯一;
  • 某一步队列有多个点,当前关系还不能唯一确定顺序。

题目要求报告首次确定或首次矛盾,所以每加入一条关系都检查,并在得到结论时立即结束。重复关系不能再次增加入度,代码用邻接矩阵先判重。

Python 知识

  • bytearray(n) 作为邻接矩阵的一行,n<=26 时紧凑且判重直接。
  • indegree.copy() 保留原入度,让每次试跑拓扑排序互不影响。
  • enumerate(relations,1) 同时得到关系和从 1 开始的输入序号。
  • "".join(chr(node+65) for node in order) 把编号转回大写字母序列。
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/collections_toolkit.mddeque 拓扑队列。
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/generator_expression.md:生成器拼接答案。

代码

python
import sys
from collections import deque


def topological_status(graph, indegree, n):
    degree = indegree.copy()
    queue = deque(node for node in range(n) if degree[node] == 0)
    order = []
    unique = True

    while queue:
        if len(queue) > 1:
            unique = False
        node = queue.popleft()
        order.append(node)
        for neighbor in range(n):
            if graph[node][neighbor]:
                degree[neighbor] -= 1
                if degree[neighbor] == 0:
                    queue.append(neighbor)

    if len(order) < n:
        return "inconsistent", order
    return ("determined" if unique else "unknown"), order


def main():
    read = sys.stdin.buffer.readline
    n, m = map(int, read().split())
    relations = [read().strip() for _ in range(m)]
    graph = [bytearray(n) for _ in range(n)]
    indegree = [0] * n

    for index, relation in enumerate(relations, 1):
        smaller, larger = relation[0] - 65, relation[2] - 65
        if not graph[smaller][larger]:
            graph[smaller][larger] = 1
            indegree[larger] += 1

        status, order = topological_status(graph, indegree, n)
        if status == "inconsistent":
            print(f"Inconsistency found after {index} relations.")
            return
        if status == "determined":
            sequence = "".join(chr(node + 65) for node in order)
            print(f"Sorted sequence determined after {index} relations: {sequence}.")
            return

    print("Sorted sequence cannot be determined.")


if __name__ == "__main__":
    main()
cpp
/**
 * P1347 排序
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-07-27 00:00
 * update_at: 2026-07-27 00:00
 */

#include <bits/stdc++.h>
using namespace std;

int n, m;
int graph[26][26];   // 邻接矩阵
int indeg[26];       // 入度

// 返回状态:0=不确定 1=确定 2=矛盾
int topo(char order[]) {
    int deg[26], idx = 0;
    memcpy(deg, indeg, sizeof(deg));
    queue<int> q;
    for (int i = 0; i < n; ++i)
        if (deg[i] == 0) q.push(i);
    bool unique = true;
    while (!q.empty()) {
        if (q.size() > 1) unique = false;
        int u = q.front(); q.pop();
        order[idx++] = u + 'A';
        for (int v = 0; v < n; ++v)
            if (graph[u][v] && --deg[v] == 0) q.push(v);
    }
    order[idx] = '\0';
    if (idx < n) return 2; // 有环 → 矛盾
    return unique ? 1 : 0; // 唯一确定 or 尚不确定
}

int main() {
    scanf("%d%d", &n, &m);
    char rel[5];
    for (int i = 1; i <= m; ++i) {
        scanf("%s", rel);
        int a = rel[0] - 'A', b = rel[2] - 'A';
        if (!graph[a][b]) {
            graph[a][b] = 1;
            ++indeg[b];
        }
        char ord[26];
        int st = topo(ord);
        if (st == 2) {
            printf("Inconsistency found after %d relations.\n", i);
            return 0;
        }
        if (st == 1 && i < m) {
            // 确定后还需要验证后续关系不矛盾
            // 但不能提前退出,因为后续可能矛盾
            // 但是题目说:确定后可直接结束
            printf("Sorted sequence determined after %d relations: %s.\n", i, ord);
            return 0;
        }
    }
    puts("Sorted sequence cannot be determined.");
    return 0;
}

复杂度

每条关系后做一次邻接矩阵拓扑排序,时间复杂度 O(mn2)O(mn^2);邻接矩阵空间复杂度 O(n2)O(n^2)。这里 n<=26,足够通过。

总结

拓扑排序不仅能判断有环,还能判断拓扑序是否唯一:观察每一步是否只有一个零入度选择即可。