道路与航线
P3008 [USACO11JAN]Roads and Planes G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意:图中有两种路一种是单向可正可负,一种是双向不负,要求单源最短路。保证单向边不形成环。
拓扑序+堆优化dij
相当于每个双向边形成团,将团缩点后形成拓扑图,根据拓扑图性质可以线性求出每个点的最短路,团内部堆优化dij。
- 对所有双向边,找出连通块,即缩点;同时记录每个点的连通块标号和每个标号连通块有哪些点。
- 对所有单向边,形成拓扑图处理;记录所有连通块的入度。
- 先将每个入度为0的连通块加入队列,每次从队头取出一个连通块,将连通块中的所有点进行堆优化dij,每次取出堆中距离最小的点,对这个点能扩展到的点如果属于同一个连通块就更新,这里就是普通堆优化dij的操作,如果不属于同一个连通块,将这个连通块的入度减一,判断如果入度为0则将连通块加入队列。
注意dij的时候每个点只做一次,因为拓扑图连通块直接的连边可能为负一个点如果做好多次就会反复更新。
一道简单题写半天
#include<bits/stdc++.h>
using namespace std;
const int N = 25010;
const int inf = 0x3f3f3f3f;
typedef pair<int, int> pii;
struct node {
int to, len;
};
vector<node> g[N];
int dist[N];
int idx = 0, id[N], du[N];
bool vis[N];
vector<int> block[N];
queue<int> tuopu;
priority_queue<pii, vector<pii>, greater<pii>> dijque;
bool book[N];
void dij() {
while (!dijque.empty()) {
pii tmp = dijque.top();
dijque.pop();
int u = tmp.second;
if(book[u]) continue;
book[u] = 1;
int siz = g[u].size();
for (int i = 0; i < siz; ++i) {
int to = g[u][i].to;
int len = g[u][i].len;
if (id[to] != id[u]) {
dist[to] = min(dist[to], dist[u] + len);
du[id[to]]--;
if (du[id[to]] == 0) tuopu.push(id[to]);
continue;
}
if (dist[to] < dist[u] + len) continue;
dist[to] = dist[u] + len;
dijque.push({dist[to], to});
}
}
}
void getBlocks(int s) {
int nwid = ++idx;
queue<int> que;
que.push(s);
vis[s] = 1;
id[s] = nwid;
block[nwid].push_back(s);
while (!que.empty()) {
int tmp = que.front();
que.pop();
int siz = g[tmp].size();
for (int i = 0; i < siz; ++i) {
int to = g[tmp][i].to;
if (vis[to]) continue;
vis[to] = 1;
id[to] = nwid;
block[nwid].push_back(to);
que.push(to);
}
}
}
int main() {
int n, r, p, s;
scanf("%d%d%d%d", &n, &r, &p, &s);
for (int i = 1, x, y, len; i <= r; ++i) {
scanf("%d%d%d", &x, &y, &len);
g[x].push_back({y, len});
g[y].push_back({x, len});
}
for (int i = 1; i <= n; ++i) {
if (vis[i]) continue;
getBlocks(i);
}
for (int i = 1, x, y, len; i <= p; ++i) {
scanf("%d%d%d", &x, &y, &len);
g[x].push_back({y, len});
du[id[y]]++;
}
memset(dist, 0x3f, sizeof dist);
dist[s] = 0;
// dij(s);
for (int i = 1; i <= idx; ++i) {
if (du[i] == 0) tuopu.push(i);
}
while (!tuopu.empty()) {
int id = tuopu.front();
tuopu.pop();
for (int u : block[id]) {
dijque.push({dist[u], u});
}
dij();
}
for (int i = 1; i <= n; ++i) {
if (dist[i] > inf / 2) printf("NO PATH\n");
else printf("%d\n", dist[i]);
}
return 0;
}