1 条题解

  • 0
    @ 2026-10-26 11:49:24

    思路与算法

    把每条原方向a到b的通道反过来,存成b到a。依次枚举目标站i,在反图从i做一次BFS;反图中能从i到达的站,恰是原图中能到达i的站。如果BFS访问数等于N,输出当前i并结束。目标按编号从小到大枚举,所以首次成功就是最小站号。若全部候选都失败,输出-1。

    正确性说明

    原图中一条从j到i的路径,反向后恰好成为反图中从i到j的路径;反向操作可逆,因此两种可达关系等价。BFS会访问起点在内的全部且仅全部可达顶点,故反图中访问N个顶点当且仅当原图中任意其他站均可到达该目标站。遍历全部候选不会遗漏合法站,递增枚举确保答案最小;没有任何候选通过时-1正确。

    复杂度

    反图建表O(N+M),其中M=N-1。每个候选初始化seen并BFS花O(N+M),最多N次,所以总时间O(N*(N+M)),在树型输入中为O(N²)。邻接表、seen和队列占O(N+M)空间。每次清空/初始化seen的O(N)已经计入。N不超过100,原1秒和内存限制下实际C++11验证全部最大规模点。

    易错点

    通道有向,不可误加无向边。要找所有其他点都能到达它的站,不是它能到所有点。反图BFS起点必须先标记,防止重复访问;N=1时无通道,答案为1。多个候选时取最小,失败输出-1。测试覆盖正向/反向长链、入星/出星、内部汇点、随机方向及最大100站。独立答案用原图Floyd闭包,并在已验证的底层树输入上用唯一汇点性质交叉检查,参考代码不依赖该性质。

    参考代码(C++11)

    #include <iostream>
    #include <queue>
    #include <vector>
    using namespace std;
    int main() {
        int n;
        cin >> n;
        vector<vector<int>> reverseEdges(n + 1);
        for (int i = 1, a, b; i < n; ++i) {
            cin >> a >> b;
            reverseEdges[b].push_back(a);
        }
        for (int goal = 1; goal <= n; ++goal) {
            vector<bool> seen(n + 1);
            queue<int> q;
            q.push(goal);
            seen[goal] = true;
            int reached = 0;
            while (!q.empty()) {
                int u = q.front();
                q.pop();
                ++reached;
                for (int v : reverseEdges[u])
                    if (!seen[v]) {
                        seen[v] = true;
                        q.push(v);
                    }
            }
            if (reached == n) {
                cout << goal << '\n';
                return 0;
            }
        }
        cout << -1 << '\n';
    }
    
    • 1

    信息

    ID
    1717
    时间
    1000ms
    内存
    64MiB
    难度
    进阶
    标签
    递交数
    1
    已通过
    1
    上传者