版权声明:原创,勿转 https://blog.csdn.net/qq_42386465/article/details/82832092
在有向图 G中,每条边的长度均为 1,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:
- 路径上的所有点的出边所指向的点都直接或间接与终点连通。
- 在满足条件1的情况下使路径最短。
注意:图 G 中可能存在重边和自环,题目保证终点没有出边。
请你输出符合条件的路径的长度。
https://www.luogu.org/problemnew/show/P2296
1、见两个图,一正一反
2、反向跑bfs,处理出所有能到达终点的点
3、再复制一个的数组,枚举每一个点,如果这个点没有被标记,则枚举它的每一条出边(反向后的),如果它指向的点被标记,则说明这个被标记的点不合法,删除。
坑点:最好有第二个数组标记,在一个数组里删点有后效型,如果一个点开始被标记,它通过一个序号比它小的点删除了, 那么访问到它的时候,就会被当成开始就没被标记的点,会通过它把合法点删除。 这样做完之后,合法点都被标记了。
4、正向跑spfa,每次松弛操作时判断一下那个点是否合法即可!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 2139062143
using namespace std;
int inline read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct node
{
int next,to;
}e1[400001],e2[400001];
int head1[100001],head2[100001],dis[100001];
bool v[100001],vv[100001],vis[100001];
int n,m,cnt1,cnt2,s,t;
queue<int> q;
void add1(int x,int y)
{
e1[++cnt1].next=head1[x];
e1[cnt1].to=y;
head1[x]=cnt1;
}
void add2(int x,int y)
{
e2[++cnt2].next=head2[x];
e2[cnt2].to=y;
head2[x]=cnt2;
}
void bfs()
{
q.push(t);
v[t]=true;
while(!q.empty()){
int now=q.front();q.pop();
for(int i=head2[now];i;i=e2[i].next){
if(!v[e2[i].to]){
v[e2[i].to]=true;
q.push(e2[i].to);
}
}
}
memcpy(vv,v,sizeof(v));
for(int i=1;i<=n;i++){
if(!v[i]){
for(int j=head2[i];j;j=e2[j].next){
if(vv[e2[j].to])
vv[e2[j].to]=false;
}
}
}
}
void spfa()
{
memset(dis,127,sizeof(dis));
q.push(s);
dis[s]=0;
vis[s]=true;
while(!q.empty()){
int now=q.front();q.pop();vis[now]=false;
for(int i=head1[now];i;i=e1[i].next){
int to=e1[i].to;
if(dis[to]>dis[now]+1&&vv[to]){
dis[to]=dis[now]+1;
if(!vis[to]){
q.push(to);
vis[to]=true;
}
}
}
}
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;i++){
int x=read(),y=read();
if(x==y) continue;
add1(x,y);add2(y,x);
}
s=read();t=read();
bfs();
spfa();
if(dis[t]==inf){
printf("-1");
}
else{
printf("%d",dis[t]);
}
return 0;
}