在有向圖 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;
}