Description
有一张边权为 \(1\) 的有向图,可能有重边自环。
求一条从起点到终点的最短路径,满足路径上的所有点的出边所指向的点都直接或间接与终点连通。
比赛的时候做了这个题,感觉是道好题。
Solution
读入的时候保险起见特判掉环,貌似不特判也可以。
首先建反向边,从终点开始走,求出有哪些点能到终点。
然后暴力求出每个点能不能加入路径,即它的所有出边指向的点都能到达终点。
然后根据边权为 \(1\),在合法的点上跑 bfs 最短路。
可以用深搜实现反向建边 + bfs 预处理,写了发现被一个环卡住了。
所以类似于记忆化搜索的 dfs 预处理,可以考虑改成 bfs。
Code
#include <bits/stdc++.h>
using namespace std;
#define re register
#define F first
#define S second
typedef long long ll;
typedef pair<int, int> P;
const int INF = 0x3f3f3f3f;
const int N = 1e4 + 5, M = 2e5 + 5;
int read() {
int x = 0, f = 0; char ch = 0;
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
struct edge{
int to, nxt;
}e[M];
int head[M], tot, dis[N];
void addedge(int x, int y){
e[++tot].to = y; e[tot].nxt = head[x]; head[x] = tot;
}
int start[N], to[N];
bool can[N], allcan[N], vis[N];
int main(){
int n = read(), m = read();
for (int i = 1; i <= m; i++){
int x = read(), y = read();
start[i] = x; to[i] = y;
if (x != y) addedge(y, x);
}
int s = read(), t = read();
queue <int> q; q.push(t);
while (!q.empty()){
int x = q.front(); q.pop(); can[x] = 1;
for (int i = head[x]; i; i = e[i].nxt){
int y = e[i].to;
if (!vis[y]){
vis[y] = can[y] = 1;
q.push(y);
}
}
}
memset(head, 0, sizeof(head)); memset(vis, 0, sizeof(vis)); tot = 0;
for (int i = 1; i <= m; i++)
if (start[i] != to[i]) addedge(start[i], to[i]);
for (int x = 1; x <= n; x++) {
allcan[x] = can[x];
for (int i = head[x]; i; i = e[i].nxt)
allcan[x] &= can[e[i].to];
}
if (allcan[s]) q.push(s), vis[s] = 1;
while (!q.empty()){
int x = q.front(); q.pop();
if (x == t){
printf("%d\n", dis[t]);
return 0;
}
for (int i = head[x]; i; i = e[i].nxt){
int y = e[i].to;
if (allcan[y] && !vis[y]){
vis[y] = 1; q.push(y);
dis[y] = dis[x] + 1;
}
}
}
puts("-1");
return 0;
}