luogu P2296 寻找道路

题目链接: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;
}
原文地址:https://www.cnblogs.com/IzumiSagiri/p/13986805.html