在一个无权图中,两个节点间的最短距离可以看成从一个节点出发到达另一个节点至少需要经过的边的个数。
同时,任意两个节点间的最短路径可能有多条,使得从一个节点出发可以有多条最短路径可以选择,并且沿着这些路径到达目标节点所经过的边的个数都是一样的。
但是在图中有那么一些特殊的节点,如果去除这些点,那么从某个初始节点到某个终止节点的最短路径长度要么变长,要么这两个节点变得不连通。这些点被称为最短路径上的关键点。
现给定一个无权图,以及起始节点和终止节点,要求输出该图上,这对节点间最短路径上的关键点数目。
思路
1. 起初把题目想简单了, 以为一遍 BFS 就能得到正解, 用案例跑了下没报错就直接提交了, 结果 WA
2. 看了解题报告发现自己 MISS 掉了很多特殊情况, 比如某个节点时关键节点但是还有与它同层但无意义的其他节点, 这就需要两遍 BFS
3. 第一遍 BFS 记录节点所在的层, 第二遍 BFS 只对层数小于当前节点的邻接节点遍历
代码
#include <iostream> #include <stdio.h> #include <deque> #include <vector> #include <memory.h> using namespace std; vector<int> graph[10005]; bool visited[10005]; int level[10005]; int n, m, s, t; void firstPass() { deque<int> queue; queue.push_back(s); visited[s] = 1; int depth = 1; level[s] = depth ++; int curlevel = 1, nextlevel = 0; while(!queue.empty()) { int p = queue.front(); queue.pop_front(); curlevel --; if(p == t) break; for(int i = 0; i < graph[p].size(); i ++) { int sp = graph[p][i]; if(visited[sp]) continue; visited[sp] = 1; queue.push_back(sp); nextlevel ++; level[sp] = depth; } if(curlevel == 0) { curlevel = nextlevel; nextlevel = 0; depth ++; } } } int secondPass() { memset(visited, 0, sizeof(visited)); deque<int> queue; queue.push_back(t); visited[t] = 1; int cnt = 0; while(!queue.empty()) { int p = queue.front(); queue.pop_front(); if(queue.size() == 0) cnt ++; if(p == s) break; for(int i = 0; i < graph[p].size(); i ++) { int sp = graph[p][i]; if(level[sp] >= level[p] || visited[sp]) continue; queue.push_back(sp); visited[sp] = 1; } } return max(cnt-2, 0); } int main() { while(scanf("%d%d%d%d", &n, &m, &s, &t) != EOF) { memset(level, 0x3f, sizeof(level)); memset(visited, 0, sizeof(visited)); for(int i = 0; i < n+3; i ++) graph[i].clear(); for(int i = 0; i < m; i ++) { int fm, to; scanf("%d%d", &fm, &to); graph[fm].push_back(to); graph[to].push_back(fm); } firstPass(); cout << secondPass() << endl; } return 0; }