解题思路
这题用\(SPFA\)会被卡,所以我们不能用\(SPFA\)
但是观察数据我们可以发现对于道路,\(0≤C_i≤10^{5}\)
所以对于每个连通块(内部不存在航线),我们可以用\(Dijkstra\)算法进行求解,因为不存在负权边,而\(Dijkstra\)算法的时间较为稳定,所以对于连通块内部的最短路,我们采用\(Dijkstra\)
块内问题解决了,但是块间呢
保证如果有一条航线可以从\(A_i\) 到 \(B_i\),那么保证不可能通过一些道路和航线从 \(B_i\) 回到 \(A_i\)。
我们将每个连通块看做一个大点
那么根据上述性质,我们可以得到这张由大点为点,航线为边的图是一张拓扑图\((DAG)\)
那么\(DAG\)的最短路我们可以按照它的拓扑序扫描求出最短路
为什么呢
因为我们利用拓扑序进行\(DP\),那后来的节点一定是无法更新前面的节点,这样就可以确保无后效性,因此可以正确求出最短路
解题步骤
1.先输入所有的双向道路,然后用\(dfs\)求出所有的连通块,用\(id[]\)数组统计每个数所在的连通块编号,用一个\(vector\)<\(int\)> \(block[]\)来保存每个编号的连通块里存的点
2.输入所有的航线,再输入的同时可以统计每个连通块的入度
3.拓扑排序
4.按照拓扑序对每个连通块进行\(Dijkstra\)算法
5.\(Dijkstra\)步骤:
(1)每次从堆中取出一个点\(t\)
(2)遍历\(t\)的所有出边,同一个连通块的进行松弛操作,不同连通块的将对方连通块入度\(-1\),如果\(-1\)后连通块的入度为0,插入拓扑序队列中(所以拓扑序的队列要开成全局的)
代码
#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 25010, M = 2e5 + 10, INF = 0x3f3f3f3f;
int h[N], e[M], w[M], ne[M], idx;
int dist[N], id[N], st[N], din[N];
int n, mr, mp, S, bcnt;
vector<int> block[M];
queue<int> q;
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++ ;
}
void dfs(int u, int bid)
{
id[u] = bid, block[bid].push_back(u);
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!id[j]) dfs(j, bid);
}
}
void Dijkstra(int bid)
{
priority_queue<PII, vector<PII>, greater<> > heap;
for (auto u : block[bid])
heap.push({dist[u], u});
while (heap.size())
{
auto t = heap.top().second; heap.pop();
if (st[t]) continue ;
st[t] = true ;
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
if (id[j] == id[t]) heap.push({dist[j], j});
}
if (id[j] != id[t] && -- din[id[j]] == 0) q.push(id[j]);
}
}
}
void topsort()
{
memset(dist, 0x3f, sizeof dist);
dist[S] = 0;
for (int i = 1; i <= bcnt; i ++ )
if (!din[i]) q.push(i);
while (q.size())
{
int t = q.front(); q.pop();
Dijkstra(t);
}
}
int main()
{
scanf("%d%d%d%d", &n, &mr, &mp, &S);
memset(h, -1, sizeof h);
memset(st, 0, sizeof st);
while (mr -- )
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(u, v, w), add(v, u, w);
}
for (int i = 1; i <= n; i ++ )
if (!id[i]) dfs(i, ++ bcnt);
while (mp -- )
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(u, v, w), din[id[v]] ++ ;
}
topsort();
for (int i = 1; i <= n; i ++ )
if (dist[i] > INF / 2) puts("NO PATH");
else printf("%d\n", dist[i]);
return 0;
}
标签:连通,航线,dist,int,bid,AcWing342,道路,heap,id
From: https://www.cnblogs.com/StkOvflow/p/16999699.html