[NOIP2014]寻找道路

思路

首先要把一些不满足条件的点剔除掉,然后就是求最短路的事情了。
要找不满足条件的点,可以反向建边,然后从终点出发,标记每一个经过的点。那么剩下没有走过的点就可以去掉了。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=10005,maxm=200005;

int cant[maxn],vis[maxn],dis[maxn];
int n,m,s,t,e,u,v;

struct E{
    int next,to;
}edge[maxm],edge2[maxm];

struct node{
    int n,w;
};
bool operator<(const node a,const node b){
    return a.w<b.w;
}

void read(int &data){
    int flag=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') flag=-flag;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    data=flag*x;
}

priority_queue<node>pq;
int head[maxn],head2[maxn],cnt=0,cnt2=0;
inline void addedge(int from,int to){
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    head[from]=cnt;

    edge2[++cnt2].next=head2[to];
    edge2[cnt2].to=from;
    head2[to]=cnt2;
}

void dfs(int x){
    vis[x]=1;
    for(int i=head2[x];i;i=edge2[i].next){
        int y=edge2[i].to;
        if(!vis[y]){
            dfs(y);
        }
    }
}

void dijistra(int s){
    memset(dis,127/3,sizeof(dis));
    if(cant[s]) return;
    pq.push((node){s,0});
    dis[s]=0;
    while(!pq.empty()){
        node fro=pq.top();
        pq.pop();
        if(dis[fro.n]!=fro.w) continue;
        for(int i=head[fro.n];i;i=edge[i].next){
            int y=edge[i].to;
            if(cant[y]) continue;
            if(dis[y]>dis[fro.n]+1){
                dis[y]=dis[fro.n]+1;
                pq.push((node){y,dis[y]});
            }
        }
    }
}
int main(){
    read(n);read(m);
    for(int i=1;i<=m;i++){
        read(u);read(v);
        if(u==v)continue; //去重边
        addedge(u,v); //建反向边
    }
    read(s);read(t);
    dfs(t);  //从终点出发,找出无法到达的点
    for(int i=1;i<=n;i++)
        if(!vis[i]) cant[i]=1;
    for(int i=1;i<=n;i++){
        if(!vis[i]) continue; 
        int flag=0;
        for(int j=head[i];j;j=edge[j].next){
            int y=edge[j].to;
            if(!vis[y]){ //如果出点被剔除,那么该点也被剔除
                flag=1;
                break;
            }
        }
        if(flag) cant[i]=1; //该数组存储最终剔除的结点
    }
    dijistra(s); //找最短路
    if(dis[t]>1e6+7) cout<<-1<<endl;
    else cout<<dis[t]<<endl;
    return 0;
}