题目链接:luogu P2296 寻找道路
题目大意:
题解:
首先想到的就是求单源最短路,所以选择用优先队列优化的Dijkstra算法。
由于存在限制:路径上的点及其子节点都必须可以到达终点。
所以首先反向建边并从终点开始BFS标记所有能够到达终点的点,再在所有标记的点中剔除存在不能到达终点的子节点的点。
最后就是在满足条件的点中跑Dijkstra。
#include <iostream>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define io_speed_up ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define N 10000 + 10
#define M 200000 + 10
int n, m, head[N], cnt, _head[N], dis[N], s, t;
bool reach[N], mark[N];
struct Edge {
int v, len, next;
} e[M], _e[M];
struct Node {
int u, dis;
bool operator < (const Node &x) const {
return dis > x.dis;
}
};
void AddEdge(int u, int v, int w) {
e[++cnt].v = v;
e[cnt].len = w;
e[cnt].next = head[u];
head[u] = cnt;
_e[cnt].v = u;
_e[cnt].len = w;
_e[cnt].next = _head[v];
_head[v] = cnt;
}
void bfs(int t) {
queue <int> q;
reach[t] = true;
q.push(t);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = _head[u]; i; i = _e[i].next) {
if (!reach[_e[i].v]) {
reach[_e[i].v] = true;
q.push(_e[i].v);
}
}
}
}
void Mark() {
for (int i = 1; i <= n; ++i) {
if (reach[i]) {
mark[i] = true;
for (int j = head[i]; j; j = e[j].next) {
if (!reach[e[j].v]) {
mark[i] = false;
break;
}
}
}
}
}
void dijkstra(int s) {
priority_queue <Node> q;
for (int i = 1; i <= n; ++i) {
dis[i] = INF;
}
dis[s] = 0;
q.push(Node{s, 0});
while (!q.empty()) {
int u = q.top().u, d = q.top().dis;
q.pop();
if (d > dis[u]) continue;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v, len = e[i].len;
if (!mark[v]) continue;
if (dis[v] > dis[u] + len) {
dis[v] = dis[u] + len;
q.push(Node{v, dis[v]});
}
}
}
}
int main() {
io_speed_up;
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int u, v;
cin >> u >> v;
AddEdge(u, v, 1);
}
cin >> s >> t;
bfs(t);
Mark();
dijkstra(s);
if (dis[t] != INF) {
cout << dis[t] << endl;
} else {
cout << -1 << endl;
}
return 0;
}