[NOIP2014 提高组] 寻找道路
题目描述
在有向图 \(G\) 中,每条边的长度均为 \(1\),现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:
- 路径上的所有点的出边所指向的点都直接或间接与终点连通。
- 在满足条件$ 1 $的情况下使路径最短。
注意:图 \(G\) 中可能存在重边和自环,题目保证终点没有出边。
请你输出符合条件的路径的长度。
输入格式
第一行有两个用一个空格隔开的整数 \(n\) 和 \(m\),表示图有 \(n\) 个点和 \(m\) 条边。
接下来的 \(m\) 行每行 \(2\) 个整数 \(x,y\),之间用一个空格隔开,表示有一条边从点 \(x\) 指向点\(y\)。
最后一行有两个用一个空格隔开的整数 \(s, t\),表示起点为 \(s\),终点为 \(t\)。
输出格式
输出只有一行,包含一个整数,表示满足题目描述的最短路径的长度。如果这样的路径不存在,输出\(-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 \le 10\),\(0 < m \le 20\);
对于\(60\%\)的数据,\(0 < n \le 100\),\(0 < m \le 2000\);
对于\(100\%\)的数据,\(0 < n \le 10000, 0 < m \le 200000,0 < x,y,s,t \le n, x,s \ne t\)。
Solution:
将与终点不连通的边删掉,在剩下的图中跑最短路即可
注意:
- 删掉终点不连通的边,即删去与终点不连通的点的入边,不是连通块
- 删边操作可反向建图,从终点遍历,无法遍历到的是与终点不连通的点
时间复杂度: \(O(n+mlogn)\)
Code:
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
typedef pair<int,int> PII;
const int N=10005,M=400005,INF=0x3f3f3f3f;
int n,m,S,T;
int idx,h[N],rh[N],dis[N];
int st[N];
struct node
{
int nxt,to;
}e[M];
void add(int h[],int nxt,int to)
{
e[++idx].nxt=h[nxt];
e[idx].to=to;
h[nxt]=idx;
}
void dfs(int u,int col)
{
if(st[u]==col)return;
st[u]=col;
for(int i=rh[u];~i;i=e[i].nxt)
{
int v=e[i].to;
dfs(v,col);
}
}
int dijk()
{
priority_queue<PII,vector<PII>,greater<PII>> q;
memset(st,0,sizeof(st));
memset(dis,0x3f,sizeof(dis));
dis[S]=0;
q.push({0,S});
while(q.size())
{
int u=q.top().second;
q.pop();
if(st[u])continue;
st[u]=1;
for(int i=h[u];~i;i=e[i].nxt)
{
int v=e[i].to;
if(dis[v]>dis[u]+1)
{
dis[v]=dis[u]+1;
q.push({dis[v],v});
}
}
}
if(dis[T]<INF)return dis[T];
return -1;
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof(h));
memset(rh,-1,sizeof(rh));
for(int i=0;i<m;++i)
{
int a,b,c;
scanf("%d%d",&a,&b);
add(rh,b,a);
}
scanf("%d%d",&S,&T);
dfs(T,1);
for(int i=1;i<=n;++i)
if(st[i]==0)
{
for(int j=rh[i];~j;j=e[j].nxt)
if(e[j].to!=S && e[j].to!=T)
st[e[j].to]=2;
st[i]=2;
}
for(int i=1;i<=n;++i)
{
if(st[i]==2)continue;
for(int j=rh[i];~j;j=e[j].nxt)
{
int v=e[j].to;
if(st[v]==2)continue;
add(h,v,i);
}
}
printf("%d\n",dijk());
return 0;
}