NOIP2014 寻找道路

题目描述

在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:

1 .路径上的所有点的出边所指向的点都直接或间接与终点连通。

2 .在满足条件1 的情况下使路径最短。

注意:图G 中可能存在重边和自环,题目保证终点没有出边。

请你输出符合条件的路径的长度。

输入输出格式

输入格式:

 

输入文件名为road .in。

第一行有两个用一个空格隔开的整数n 和m ,表示图有n 个点和m 条边。

接下来的m 行每行2 个整数x 、y ,之间用一个空格隔开,表示有一条边从点x 指向点y 。

最后一行有两个用一个空格隔开的整数s 、t ,表示起点为s ,终点为t 。

 

输出格式:

 

输出文件名为road .out 。

输出只有一行,包含一个整数,表示满足题目᧿述的最短路径的长度。如果这样的路径不存在,输出- 1 。

 

输入输出样例

输入样例#1:  复制
3 2  
1 2  
2 1  
1 3  
输出样例#1:  复制
-1
输入样例#2:  复制
6 6  
1 2  
1 3  
2 6  
2 5  
4 5  
3 4  
1 5  
输出样例#2:  复制
3

说明

解释1:

如上图所示,箭头表示有向道路,圆点表示城市。起点1 与终点3 不连通,所以满足题

目᧿述的路径不存在,故输出- 1 。

解释2:

如上图所示,满足条件的路径为1 - >3- >4- >5。注意点2 不能在答案路径中,因为点2连了一条边到点6 ,而点6 不与终点5 连通。

对于30%的数据,0<n≤10,0<m≤20;

对于60%的数据,0<n≤100,0<m≤2000;

对于100%的数据,0<n≤10,000,0<m≤200,000,0<x,y,s,t≤n,x≠t。

 

思路:当我第一眼看这道题时,我原以为D2T2肯定比较难,结果再看了看发现:WTF?这真的是D2T2难度??哎呀不说了,大水题一个。

想法很简单,既然要判断每个点与终点是否联通,(以下为我一本正经的口胡)我们可以考虑用并查集(这样真的好吗?),但是这是有向图,那怎么办呢?我们发现,终点是不变的,而且只需判断终点与其他点

的联通性,那我们可以用一个技巧:把所有的边都反向,然后从终点跑一遍BFS,遍历全图,这样,没有被访问过的点就一定与终点不连通。为什么要用这种方法呢?因为如果边是正向,那么我们定会让每个点都跑

一遍BFS,无疑会TLE,考虑到从一个点出发能到达所有联通的点,那么我们可以反向BFS(实质是原来的起点与终点互换位置)。

处理完联通性后,我们对所有的与终点不连通的点进行反向出边遍历(名词是自己造的QAQ),对遍历到的点标记为不合法(因为我们这条路要满足条件1),这样的话,再从起点s正向跑一遍SPFA,当遇到不合法的

点时就跳过,然后dist[ed]或-1就是最后的答案。(到达不了就是-1)。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int N=10000+5;
const int M=2e5+5;
int read()
{
    int ret=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9')
    {ret=ret*10+c-'0';c=getchar();}
    return ret*f;
}
int n,m;
struct edge{
    int from,to;
}e1[M],e2[M];
int head1[N],nxt1[M],tot1=0;
int head2[N],nxt2[M],tot2=0;
bool vis[N],np[N];
void adde(int f,int t)
{
    e1[++tot1]=(edge){f,t};
    e2[++tot2]=(edge){t,f};
    nxt1[tot1]=head1[f];
    nxt2[tot2]=head2[t];
    head1[f]=tot1;
    head2[t]=tot2;
}
queue<int > q;
int dist[N];
bool inq[N];
void bfs(int s)
{
    vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head2[u];i;i=nxt2[i])
        {
            int v=e2[i].to;
            if(!vis[v]) q.push(v),vis[v]=1;
        }
    }
}
void solve()
{
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            for(int j=head2[i];j;j=nxt2[j])
            {
                int v=e2[j].to;
                np[v]=1;
            }
        }
    }
}

void spfa(int s)
{
    dist[s]=0;
    q.push(s);
    inq[s]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        inq[u]=0;
        for(int i=head1[u];i;i=nxt1[i])
        {
            int v=e1[i].to;
            if(np[v]) continue;
            if(dist[v]>dist[u]+1)
            {
                dist[v]=dist[u]+1;
                if(!inq[v])
                {
                    q.push(v);
                    inq[v]=1;
                }
            }
        }
    }
}
int main()
{
    memset(dist,0x3f,sizeof(dist));
    n=read(),m=read();
    int a,b;
    for(int i=1;i<=m;i++)
    {
        a=read(),b=read();
        adde(a,b);
    }
    int st,ed;
    st=read(),ed=read();
    bfs(ed);
    solve();
    spfa(st);
    int ans=(dist[ed]>1e9?-1:dist[ed]);
    printf("%d\n",ans);
    return 0;
}