题目大意
给定n个点的图
有两种类型的边,一种是双向边,保证非负
一种是单向边,可能存在负值,但保证负边不成环
问从规定s节点出发后,到达每个点的最短距离
解法
因为保证了单向边不成环,说以我们可以先处理双向边,并将其缩为一个点
然后再在有点向边的基础上,对于全部的单向边和缩的点跑拓扑排序,对于每个缩的点跑dijkstra
具体过程如下:
1.只把双向边加入图中,用dfs处理出图的连通块,计color[x]为节点x所在的连通块编号
2.读取双向边信息,统计每个连通块的总入度,记deg[i]为第i个连通块的总入度
3.建立一个队列(储存连通块编号,用于拓扑排序),最开始将所有入度为0的连通块编号加入到这个队列
4.去出队首的连通块编号,对这个连通块进行dijkstra算法
(1).建立一个小根堆,将这个连通块的所有节点加入这个堆里面
(2).取出堆顶元素u,若被扩展,返回上一步
(3).遍历u的所有相连节点,用dist[u,v]+dist[u]来更新dist[v]
(4).若color[u]==color[v],将v加入堆中
(5).若color[u]!=color[v],将v对应连通块的入度减1。若减到0,将这个连通块编号加入拓扑队列
(6).重复(2)~(5),直到堆为空
5.重复4,直到队列为空
再此过程中应该特别注意,最后判断输出NO PATH的时候一定不要直接判等于inf,应该比inf小才能得到正确答案
样例如下
3 0 1 1
2 3 -100
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 25010;
const int M = 150010;
struct node
{
int dist, to;
int next;
};
int t, r, p, s, k;
int head[N];
node edge[M];
int color[N];
int cnt_block;
vector<int> block[N];
int deg[N];
queue<int> que;
int dist[N];
bool vis[N];
void add(int come, int to, int dist)
{
edge[++k] = {dist, to, head[come]};
head[come] = k;
}
void dfs(int u, int cnt)
{
block[cnt].push_back(u);
color[u] = cnt;
for (int i = head[u]; i; i = edge[i].next)
{
int v = edge[i].to;
if (!color[v])
dfs(v, cnt);
}
}
struct node2
{
int to, dist;
bool friend operator<(node2 a, node2 b)
{
return a.dist > b.dist;
}
};
void dij(int now_block)
{
priority_queue<node2> q;
for (auto u : block[now_block])
q.push(node2{u,dist[u]});
int u, v, w;
while (!q.empty())
{
u = q.top().to;
q.pop();
if (vis[u])
continue;
vis[u] = true;
for (int i = head[u]; i; i = edge[i].next)
{
v = edge[i].to;
w = edge[i].dist;
if (dist[v] > dist[u] + w)
{
dist[v] = dist[u] + w;
if (color[u] == color[v])
q.push(node2{v,dist[v]});
}
if (color[u] != color[v] && --deg[color[v]] == 0)
que.push(color[v]);
}
}
}
void topo_sort()
{
memset(dist, 0x3f, sizeof(dist));
dist[s] = 0;
for (int i = 1; i <= cnt_block; i++)
if (deg[i] == 0)
que.push(i);
while (!que.empty())
{
int temp = que.front();
que.pop();
dij(temp);
}
}
int main()
{
scanf("%d%d%d%d", &t, &r, &p, &s);
for (int i = 1, u, v, w; i <= r; i++)
{
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
for (int i = 1; i <= t; i++)
if (color[i] == 0)
dfs(i, ++cnt_block);
for (int i = 1, u, v, w; i <= p; i++)
{
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
deg[color[v]]++;
}
topo_sort();
for (int i = 1; i <= t; i++)
{
if (dist[i] >= 0x3f3f3f3f / 2) //特别注意
printf("NO PATH\n");
else
printf("%d\n", dist[i]);
}
return 0;
}