先看题目:https://ac.nowcoder.com/acm/problem/16498
题目描述:
在有向图G中,每条边的长度均为1,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:
1.路径上的所有点的出边所指向的点都直接或间接与终点连通。
2.在满足条件1的情况下使路径最短。
注意:图G中可能存在重边和自环,题目保证终点没有出边。
请你输出符合条件的路径的长度。
解题思路:
看到感觉是个图论有点慌,但其实bfs就搞定了
用了两次bfs(作用不同)
参考savage的博客
1.首先预处理,反向建图,从终点出发,标记所有终点能够直接或间接到达的点。
2.然后遍历所有的点,未被标记的点也就是原来图中直接或间接到达不了终点的点。
3.依据题意,与这些点直接相连的那些被标记的点现在就不能走了。你会怎么标记呢?这里要注意后效性。原来标记了假设为vis[i]=1;现在i点因为与不能到达终点的某个点相连,应该取消标记。但取消标记了后的含义就是i这个点不能到达终点了,这样与i相连的就也都被取消标记了...但这显然是错的,因为i是可以到达终点的。用另外一个标记数组vis2在第一遍bfs后memcpy一下vis数组,然后跟据vis的情况对vis2进行调整就避免了后效性。
4.在被标记的点中bfs去求单源最短路。
代码
#include<bits/stdc++.h> using namespace std; const int maxn=10010; vector<int> G[maxn]; int n, m, s, t; queue<int> q; int vis[maxn]; int vis2[maxn]; int dis[maxn]; void bfs() { q.push(t); vis[t]=1; while(!q.empty()) { int tmp = q.front(); q.pop(); for(int i = 0; i < G[tmp].size(); i++) { if(vis[G[tmp][i]]) continue; vis[G[tmp][i]]=1; q.push(G[tmp][i]); } } } int bfs2() { while(!q.empty()) q.pop(); q.push(t); dis[t] = 0; while(!q.empty()) { int tmp = q.front(); q.pop(); for(int i = 0; i < G[tmp].size(); i++) { if(vis2[G[tmp][i]] == 0) continue; if(dis[G[tmp][i]] != -1) continue; dis[G[tmp][i]] = dis[tmp]+1; if(G[tmp][i] == s) return 1; q.push(G[tmp][i]); } } return 0; } int main() { memset(vis, 0, sizeof(vis)); memset(dis, -1, sizeof(dis)); scanf("%d%d",&n,&m); while(m--){ int x, y; scanf("%d%d",&x,&y); G[y].push_back(x); //反向 } scanf("%d%d",&s,&t); bfs(); memcpy(vis2, vis, sizeof(vis)); for(int i = 1; i <= n; i++) { if(vis[i] == 0) { for(int j = 0; j < G[i].size(); j++) { //后效性注意! vis2[G[i][j]] = 0; } } } if(bfs2()) { printf("%d\n",dis[s]); } else { printf("-1\n"); } }