道路与航线–dfs+topsort+dijkstra+建图方式
算法流程
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;
typedef long long LL;
typedef pair<int, int> PII;
#define x first
#define y second
const int N = 25010,M=150010;
int h[N],e[M],ne[M],w[M],idx=0;
int dist[N];
bool st[N];
vector<int>block[N];
int bcnt=0;
int id[N];
int n,rm,pm,s;
int din[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)
{
memset(st, 0, sizeof st);
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;
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]==0)
{
q.push(i);
}
}
while(q.size())
{
int t=q.front();
q.pop();
dij(t);
}
}
int main()
{
memset(h,-1,sizeof h);
cin>>n>>rm>>pm>>s;
int a,b,c;
while(rm--)
{
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
for(int i=1;i<=n;i++)
{
if(!id[i])
{
dfs(i,++bcnt);
}
}
while(pm--)
{
cin>>a>>b>>c;
din[id[b]]++;
add(a,b,c);
}
topsort();
for(int i=1;i<=n;i++)
{
if(dist[i]>0x3f3f3f3f/2)cout<<"NO PATH"<<endl;
else cout<<dist[i]<<endl;
}
}