题目:P3008 [USACO11JAN]Roads and Planes G
题目链接:https://www.luogu.com.cn/problem/P3008
题目描述:
输入格式:
第一行包含四个整数 T,R,P,S。接下来 R 行,每行包含三个整数(表示一个道路)Ai,Bi,Ci。接下来 P 行,每行包含三个整数(表示一条航线)Ai,Bi,Ci。
输出格式:
第 1…T 行:第 i 行输出从 S 到达城镇 i 的最小花费,如果不存在,则输出 NO PATH。
6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10
答案:
NO PATH
NO PATH
5
0
-95
-100
样例图:
算法流程:
- 1.先输入所有的双向道路,然后通过DFS求出所有的连通块,用两个数组来存储团的和团映射的编号:id数组存储每个点属于哪个团,vectorblock[] 来存储每个团里有哪些点;
- 2.然后输入所有的航线,同时统计出每个团的入度(因为航线是单向的),以便做拓扑排序。
- 3.按照拓扑序列依次处理每个团。先将入度为0的团的编号加入到队列中。
- 4.每次在队友取出一个团的编号bid
- 5.然后将block[bid]中的所有点加入到堆中,然后对堆中的所有点跑dijkstra算法
- 6.这里跑的是堆优化的dijkstra算法,因为一般的dijkstra算法的时间复杂度为n方,而堆优化的是nlogn的
- 7.每次在堆中取出距离最小的点u,然后遍历它能够到达的所有点j。一、如果dist[j] > dist[u] + w[i],然后更新距离,如果u 和 j在同一个团中,那么将j加入到堆中;二、如果他们不属于同一个团,那么j所在的团的入度就减少1,如果id[j]的入度为0的话,那么把所在的团的编号加入到队列中。
样例通过算法解释:
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
const int N = 25010, M = 150010, INF = 0x3f3f3f3f;
int h[N], e[M], ne[M], w[M], idx;
int dist[N];
bool st[N];
int n, mr, mp, s;
//id:点所在的连通块号, bcnt:连通块数量
int id[N], bcnt;
//连通块的入度数组
int deg[N];
vector<int> block[N];
int q[N], hh, tt = -1;
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)
{
block[bcnt].push_back(u);
id[u] = bcnt;
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if(!id[j]) dfs(j);
}
}
void dijkstra(int block_id)
{
priority_queue<PII, vector<PII>, greater<PII>> heap;
//取出该连通块所有的点, 入堆
for(int u : block[block_id])
{
heap.push({dist[u], u});
}
while(heap.size())
{
PII t = heap.top();
heap.pop();
int u = t.second;
if(st[u]) continue;
st[u] = true;
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if(dist[j] > dist[u] + w[i])
{
dist[j] = dist[u] + w[i];
if(id[j] == block_id) heap.push({dist[j], j});
}
if(id[j] != block_id && --deg[id[j]] == 0) q[++tt] = id[j];
}
}
}
void topoSort()
{
//dist赋初值
memset(dist, 0x3f, sizeof dist);
dist[s] = 0;
//入度为0的连通块入队列
for(int i = 1; i <= bcnt; i++)
{
if(!deg[i]) q[++tt] = i;
}
while(hh <= tt)
{
int t = q[hh++];
dijkstra(t);
}
}
int main()
{
scanf("%d%d%d%d", &n, &mr, &mp, &s);
memset(h, -1, sizeof h);
for(int i = 0; i < mr; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
//建立所有的连通块
for(int i = 1; i <= n; i++)
{
if(!id[i]) {
++bcnt;
dfs(i);
}
}
//在连通块之间建边
for(int i = 0; i < mp; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), deg[id[b]]++;
}
//算法开始
topoSort();
for(int i = 1; i <= n; i++)
{
if(dist[i] > INF / 2) puts("NO PATH");
else printf("%d\n", dist[i]);
}
return 0;
}