先看题目: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");
    }
}