题意
传送门 P3008 [USACO11JAN]Roads and Planes
题解
图中双向边非负权边,仅单向边可能是负权边且不构成环。那么对于双向边构成的连通分量,可以使用 D i j k s t r a Dijkstra Dijkstra 求解,然后将连通分量看做点,在得到的 D A G DAG DAG 上拓扑序求最短路。
那么在拓扑序的框架上进行求解。将入度为 0 0 0 的连通分量以及 S S S 所在的连通分量入队,显然入度为 0 0 0 且不包含 S S S 的连通分量无法从 S S S 可达,但处理拓扑序的时候按照完整图进行入度统计,若不将这些显然不可达的连通分量入队,可能无法得到正确答案。每次出队的连通分量的所有节点都被拓展过,将其全部加入小根堆中,使用 D i j k s t r a Dijkstra Dijkstra 进行求解。
需要注意的是,求解最短路时对距离数组初始化的极大值,需要满足大于可能的最大边权和,加上可能的最小边权和时大于可能的最大边权和,且加上可能的最大边权和不会溢出。
#include <bits/stdc++.h>
using namespace std;
#define maxv 25005
#define inf 600000000
typedef pair<int, int> pii;
struct edge
{
int v, w;
};
int V, R, P, S, C[maxv], D[maxv], deg[maxv];
bool used[maxv];
vector<edge> G[maxv];
vector<int> vC[maxv];
void dfs(int v, int c)
{
C[v] = c, vC[c].push_back(v);
for (auto &e : G[v])
if (!C[e.v])
dfs(e.v, c);
}
int main()
{
scanf("%d%d%d%d", &V, &R, &P, &S);
for (int i = 0; i < R; ++i)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
G[u].push_back(edge{v, w});
G[v].push_back(edge{u, w});
}
int c = 0;
for (int v = 1; v <= V; ++v)
if (!C[v])
dfs(v, ++c);
for (int i = 0; i < P; ++i)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
G[u].push_back(edge{v, w});
++deg[C[v]];
}
queue<int> Q;
priority_queue<pii, vector<pii>, greater<pii>> q;
memset(D, 0x3f, sizeof(D));
D[S] = 0;
Q.push(C[S]);
for (int i = 1; i <= c; ++i)
if (!deg[i])
Q.push(i);
while (!Q.empty())
{
int c = Q.front();
Q.pop();
for (auto &v : vC[c])
q.push(pii(D[v], v));
while (!q.empty())
{
int v = q.top().second;
q.pop();
if (used[v])
continue;
used[v] = 1;
for (auto &e : G[v])
{
if (D[e.v] > D[v] + e.w)
{
D[e.v] = D[v] + e.w;
if (C[v] == C[e.v])
q.push(pii(D[e.v], e.v));
}
if (C[e.v] != C[v] && --deg[C[e.v]] == 0)
Q.push(C[e.v]);
}
}
}
for (int v = 1; v <= V; ++v)
if (D[v] >= inf)
puts("NO PATH");
else
printf("%d\n", D[v]);
return 0;
}