算法流程
1.先输入所有双向道路,然后DFS
出所有连通块,计算两个数组:id[]
存储每个点属于哪个连通块;vector<int> block[]
存储每个连通块里有哪些点:
2.输入所有航线,同时统计出每个连通块的入度。
3.按照拓扑序依次处理每个连通块,先将所有入度为0
的连通块的编号加入队列
中。
4.每次从队头取出一个连通块的编号bid
5.将该block[bid]
中的所有点加入堆中,然后对堆中所有点跑dijkstra
算法。
6.每次取出堆中距离最小的点ver
。
7.然后遍历ver
的所有邻点j
。如果id[ver]==id[]
,那么如果j能被更新,则将j
插入堆中:如果id[ver]!= id[j]
,则将id[j]
这个连通块的入度减1
,如果减成0
了,则将其插入拓扑排序的队列中
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int N = 25010,M=150010,INF=0x3f3f3f3f;
typedef pair<int, int> PII;
#define x first
#define y second
int e[M],h[N],ne[M],w[M],idx=0;
int n,mr,mp,s;
int bcnt;
vector<int>block[N];
int id[N];
int din[N];
int dist[N];
bool st[N];
queue<int>q;
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 bid)
{
id[u]=bid,block[bid].push_back(u);
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(!id[j])
dfs(j,bid);
}
}
void dij(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.y,distance=t.x;
if(st[ver])continue;
st[ver]=true;
for(int i=h[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(id[j]!=id[ver]&&--din[id[j]]==0)q.push(id[j]);
if(dist[j]>dist[ver]+w[i]){
dist[j]=dist[ver]+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(!din[i])
q.push(i);
while(q.size())
{
int t=q.front();
q.pop();
dij(t);
}
}
signed main()
{
memset(h, -1, sizeof h);
cin>>n>>mr>>mp>>s;
for(int i=1;i<=mr;i++)
{
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);
}
for(int i=1;i<=mp;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
din[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;
}