题解 P2296 【寻找道路】

· · 题解

这道题的难点在于“路径上的所有点的出边所指向的点都直接或间接与终点连通”这句话,意思就是说一个点是合法的,当且仅当其所有子节点都与终点联通。但是,一个点是合法的,不代表他的子节点都是合法的

理解了这句话后,可以容易的想到所有【不合法】的点是【不与终点联通的点】+【不与终点联通的点的所有子结点(哪怕该子节点与终点联通也不合法)】于是,三步解决问题。

1.反向bfs找到所有不与终点联通的点。

2.用for循环遍历所有不与终点联通的点并将其子节点也找出来(同样反向)

3.正向bfs从s->t

代码如下:

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
vector<int> edge[10005];//正边
vector<int> anti_edge[10005];//反边
int connect[10005];//connect[i]表示i点是否与终点联通      1联通     0不联通
int true_connect[10005];// true_connect[i]表示i点是否合法       1合法      0不合法
int visit[10005];//小优化,避免第三步的正向bfs陷入环中。没有也能过
int s,t;
struct node{
    int now;
    int d;
    node(int n1,int d1): now(n1),d(d1) {}
};
int main(){
int n,m; cin>>n>>m;
for(int i=1;i<=m;i++){
    int u,v; cin>>u>>v;
    if(u==v) continue;
    edge[u].push_back(v);
    anti_edge[v].push_back(u);
}
cin>>s>>t;
//从终点逆向bfs 
queue<int> q1;
q1.push(t);
while(!q1.empty()){
    int now=q1.front(); q1.pop();
    connect[now]=1;
    for(int i=0;i<anti_edge[now].size();i++){
        int to=anti_edge[now][i];
        if(connect[to]==0) q1.push(to);
    }
} 
for(int i=1;i<=n;i++) true_connect[i]=1;
for(int i=1;i<=n;i++){
    if(connect[i]==0){
        true_connect[i]=0;
        for(int j=0;j<anti_edge[i].size();j++){
            int to=anti_edge[i][j];
            true_connect[to]=0;
        }
    }     
} 
queue<node> q2;
q2.push( node(s,0) );
while(!q2.empty()){
    node n=q2.front(); q2.pop();
    int now=n.now;
    int d=n.d;
    visit[now]=1;
    if(now==t) { cout<<d; return 0;    }
    for(int i=0;i<edge[now].size();i++){
        int to=edge[now][i];
        if(true_connect[to]==1 && visit[to]==0 )    q2.push( node(to,d+1) );
    }    
}
    cout<<-1; 
    return 0;
}