写在前面:
这应该是2019CSP之前写的最后一篇题解吧
关于码风等细节说明:
-
用了链式前向星
-
码风奇异,是蒟蒻风的
-
\(n^2\)能过,没加堆优化
思路:
-
反向建边,从终点跑一次dijkstra
-
inf值的删掉自己和出边是到自己的点
-
正常的建边从起点跑一次dijkstra
详细解析:
就是因为条件一导致我不得套板子!我要把条件一吞掉!
怎么吞呢?我们只保留那些所有出边所指向的点都直接或间接与终点连通的点即可
我们可以反向建图,从终点出发跑一边dijkstra
这样所有距离终点为inf的就无法到达终点,删掉它们!
(开个bool数组flag,将它们的flag值设为1)
把出边到距离终点是inf的点删掉,仍然使用flag数组可能会多删一些点怎么办?
用bool数组tmp存储一开始的flag数组的值,更新全部更新到flag数组
for(int i=1;i<=n;i++)
if(dist[i]==inf)
tmp[i]=flag[i]=1;
for(int i=1;i<=m;i++)
if(tmp[V[i]]) flag[U[i]]=1;
接下来就只要在剩下的图上愉快地跑dijkstra了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
int n,m,cnt,s,t;
int hd[10005],dist[10005];
bool vis[100005],flag[100005],tmp[100005];
//vis[i]:i点是否已被确定最短路,flag[i]:i点是否被删
int U[200005],V[200005],to[200005],nex[200005];
void add(int u,int v){
to[++cnt]=v;
nex[cnt]=hd[u];
hd[u]=cnt;
}
void dijkstra(int from){
memset(dist,0x3f,sizeof(dist));
int cur=from;
dist[from]=0;
while(!vis[cur]){
vis[cur]=1;
for(int i=hd[cur];i;i=nex[i]){
int v=to[i];
if(dist[v]>dist[cur]+1)
dist[v]=dist[cur]+1;
}
int minn=inf;
for(int i=1;i<=n;i++){
if(!flag[i]&&!vis[i]){
if(minn>dist[i]){
minn=dist[i];
cur=i;
}
}
}
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&U[i],&V[i]);
add(V[i],U[i]);
}
scanf("%d%d",&s,&t);
dijkstra(t);
for(int i=1;i<=n;i++)
if(dist[i]==inf)
tmp[i]=flag[i]=1;
cnt=0;
memset(vis,0,sizeof(vis));
memset(hd,0,sizeof(hd));
memset(to,0,sizeof(to));
memset(nex,0,sizeof(nex));
for(int i=1;i<=m;i++)
if(tmp[V[i]]) flag[U[i]]=1;
for(int i=1;i<=m;i++)
if(!flag[U[i]]&&!flag[V[i]])
add(U[i],V[i]);
dijkstra(s);
if(dist[t]==inf) printf("-1\n");
else printf("%d\n",dist[t]);
return 0;
}