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;
}