道路与航线
题意
有一些有向边,还有一些无向边,已知不存在回路,求起点到其他各点的最短距离。
数据范围
卡spfa
思路(参考acwing)
这题脑洞比较大,但是思想较为深刻。因为不存在回路,所以可以将一些通过无向边相连的点看成一个块,然后这些块通过有向边形成一个有向图。也就是说,局部是无向图,全局是有向图。
然后,对全局做拓扑排序,块的内部做Dijkstra。算法步骤如下:
- 先输入所有双向道路,然后等方式求出所有连通块,计算出两个数组:每个点属于哪个连通块,每个连通块有哪些点。
- 输入所有航线,同时统计出每个连通块的入度。
- 按照拓扑序一次处理每个连通块。先将所有入度为0的连通块的编号加入队列中。
- 每次从队头取出一个连通块的编号bid
- 将该块中的所有点加入堆中,然后对堆中所有点跑Dijkstra算法。
- 每次取出堆中距离最小的点ver
- 然后遍历ver的所有邻点j,如果id[ver] == id[j],那么如果j能被更新,则将j插入堆中;如果id[ver] != id[j],则将id[j]这个连通块的入度减1,如果减成0了,则将其插入拓扑排序的队列中。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int N = 25010, M = 150010, inf = 0x3f3f3f3f;
typedef pair<int,int> pii;
int n,R,P,S;
int h[N], e[M], ne[M], w[M], idx;
int dist[N], id[N];
bool st[N];
int bin[N];
int bcnt;
vector<int> block[N];
queue<int> que;
void add(int a,int b,int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
void dfs(int u,int k)
{
block[k].push_back(u);
id[u] = bcnt;
for(int i=h[u];~i;i=ne[i]){
int j = e[i];
if(!id[j]){
dfs(j,k);
}
}
}
void dijkstra(int bid)
{
priority_queue<pii,vector<pii>,greater<pii>> heap;
for(auto u:block[bid]){
heap.push({dist[u],u});
}
while(heap.size()){
auto t = heap.top();
heap.pop();
int ver = t.second, distance = t.first;
if(st[ver]) continue;
st[ver] = true;
for(int i=h[ver];~i;i=ne[i]){
int j = e[i];
if(id[j]!=id[ver]&&--bin[id[j]]==0) que.push(id[j]);
if(dist[j]>distance+w[i]){
dist[j] = distance + w[i];
if(id[j]==id[ver]){
heap.push({dist[j],j});
}
}
}
}
}
void topsort()
{
memset(dist,0x3f,sizeof(dist));
dist[S] = 0;
for(int i=1;i<=bcnt;i++)
if(!bin[i])
que.push(i);
while(que.size()){
int t = que.front();
que.pop();
dijkstra(t);
}
}
int main()
{
cin >> n >> R >> P >> S;
memset(h,-1,sizeof(h));
while(R--){
int a,b,c;
cin >> a >> b >> c;
add(a,b,c), add(b,a,c);
}
for(int i=1;i<=n;i++)
if(!id[i])
{
bcnt ++;
dfs(i,bcnt);
}
while(P--){
int a,b,c;
cin >> a >> b >> c;
add(a,b,c);
bin[id[b]] ++;
}
topsort();
for(int i=1;i<=n;i++){
if(dist[i]>inf/2) cout << "NO PATH" << endl;
else cout << dist[i] << endl;
}
return 0;
}