1 条题解
-
0
思路与算法
把每条原方向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