链接:P2296
题目描述
在有向图 GG 中,每条边的长度均为 11,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:
1 路径上的所有点的出边所指向的点都直接或间接与终点连通。
2 在满足条件 1的情况下使路径最短。
注意:图 GG 中可能存在重边和自环,题目保证终点没有出边。
请你输出符合条件的路径的长度。
输入格式
第一行有两个用一个空格隔开的整数 nn 和 mm,表示图有 nn 个点和 mm 条边。
接下来的 mm 行每行 22 个整数 x,yx,y,之间用一个空格隔开,表示有一条边从点 xx 指向点yy。
最后一行有两个用一个空格隔开的整数 s, ts,t,表示起点为 ss,终点为 tt。
输出格式
输出只有一行,包含一个整数,表示满足题目描述的最短路径的长度。如果这样的路径不存在,输出-1−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
数据范围
对于30%30%的数据,0 < n \le 100<n≤10,0 < m \le 200<m≤20;
对于60%60%的数据,0 < n \le 1000<n≤100,0 < m \le 20000<m≤2000;
对于100%100%的数据,0 < n \le 10000, 0 < m \le 200000,0 < x,y,s,t \le n, x,s \ne t0<n≤10000,0<m≤200000,0<x,y,s,t≤n,x,s≠t。
我们要求最短路,但是加了一个前提条件:
路径上的所有点的出边所指向的点都直接或间接与终点连通。
那么我们就需要预处理一下,由于又是判断两个节点是否连通的问题。
总结判断两个节点是否联通的方法:
1.使用并查集 (适合无向图处理)
2.使用最短路
3.采用BFS和DFS预处理
由于本题要求的是有向图,那么我们就可以采用SPFA建立一个反向图来判断终点到各个点是否连通,这样就可以省下一点时间了。然后我们在第二个SPFA求起点到终点的最短路时判断一下当前与这个点相连的点是否与终点相连即可。
代码:
#include <cstdio>
#include <iostream>
using namespace std;
struct CZP
{
int next,to,dis;
}a[200001],b1[200001];
int que[20000001],b[100001],dis[100001],h[100001],h1[100001],dis1[100001];
int top,top1,m,n,sx,ex;
void cun(int from,int to)
{
a[++top].next=h[from];
a[top].to=to;
a[top].dis=1;
h[from]=top;
} //邻接表存储
void cun1(int from,int to)
{
b1[++top1].next=h1[from];
b1[top1].to=to;
b1[top1].dis=1;
h1[from]=top1;
} //建立一个反向图用于判断各点与终点是否连通
int pd(int x)
{
int k=h[x];
while (k!=-1)
{
if (dis1[a[k].to]>=1e9)
return 0;
k=a[k].next;
} //判断与其相连的点是否与终点相连,即反向图的最短路是否是初始化的最大值
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
h1[i]=-1,h[i]=-1;
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
cun(x,y);
cun1(y,x);
}
scanf("%d%d",&sx,&ex);
int head=0,tail=1;
for (int i=1;i<=n;i++)
dis1[i]=1e9;
dis1[ex]=0;
b[ex]=1;
que[1]=ex;
do
{
head++;
int v=que[head];
int k=h1[v];
b[v]=0;
while (k!=-1)
{
if (dis1[b1[k].to]>dis1[v]+b1[k].dis)
{
dis1[b1[k].to]=dis1[v]+b1[k].dis;
if (!b[b1[k].to])
{
tail++;
que[tail]=b1[k].to;
b[b1[k].to]=1;
}
}
k=b1[k].next;
}
}while (head<tail); //第一个SPFA用于判断各点是否与终点相连
head=0,tail=1;
for (int i=1;i<=n;i++)
dis[i]=1e9,b[i]=0;
dis[sx]=0;
b[sx]=1;
que[1]=sx;
do
{
head++;
int v=que[head];
int k=h[v];
b[v]=0;
while (k!=-1)
{
if (dis[a[k].to]>dis[v]+a[k].dis && pd(a[k].to)) //如果由相连的点与终点不相连则不更新起点到这个点的最短距离并且不入队列
{
dis[a[k].to]=dis[v]+a[k].dis;
if (!b[a[k].to])
{
tail++;
que[tail]=a[k].to;
b[a[k].to]=1;
}
}
k=a[k].next;
}
}while (head<tail);
if (dis[ex]>=1e9) //特判: 如果起点与终点不连通
printf("-1");
else
printf("%d",dis[ex]);
return 0;
}