342. 道路与航线
思路:
看起来像是道最短路模板题,但是
s
p
f
a
spfa
spfa 会
T
T
T 掉,优化的
s
p
f
a
spfa
spfa 好像可以过掉这个题(ACwing时限大了
不过我们这里不讨论优化的
s
p
f
a
spfa
spfa
题目限制比较奇妙,双向边的权值是非负的,单向边的权值可能为负,但是单向边不会构成环
如果我们把所有由双向边构成的连通块看成一个点,那么之后添加完单向边就构成了一个有向无环图
对于
D
A
G
DAG
DAG,我们可以用拓扑排序很快的求出单源最短路
而对于每个连通块,我们可以用
D
i
j
k
s
t
r
a
Dijkstra
Dijkstra 解决
看完题解感觉好像懂了,首先
d
f
s
dfs
dfs 对连通块染色,然后对连通块建有向图跑拓扑排序求最短路
这些听起来都很合理
然后就是对于每个连通块执行
D
i
j
k
s
t
r
a
Dijkstra
Dijkstra 算法,具体步骤可以看代码
它把所有点都先
p
u
s
h
push
push 进来的行为我不是非常能理解
然后它的拓扑排序更新是在执行
D
i
j
Dij
Dij 的过程中顺便进行的
我大受震撼,不过确实有点道理
这个过程真的好抽象
样例的图大概是这样的
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 3e4 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn * 10];
int head[maxn], cnt;
int color[maxn], dcnt;
int in[maxn];
bool vis[maxn];
ll dis[maxn];
vector <int> d[maxn];
queue <int> q1;
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
void dfs(int x){
color[x] = dcnt;
d[dcnt].push_back(x);
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(color[to]) continue;
dfs(to);
}
}
void dij(int s){
priority_queue <pair<ll,int>,vector<pair<ll,int> >, greater<pair<ll,int> > > q;
for(int x : d[s]) q.push({dis[x], x});
while(!q.empty()){
int x = q.top().second;q.pop();
if(vis[x]) continue;
vis[x] = 1;
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(dis[to] > dis[x] + e[i].w){
dis[to] = dis[x] + e[i].w;
if(color[to] == s) q.push({dis[to], to});
}
if(color[to] != s) {
in[color[to]]--;
if(!in[color[to]]) q1.push(color[to]);
}
}
}
}
void topsort(int s){
mem(dis, 0x3f);dis[s] = 0;// 保证所有的dis[x]都是由s转移来的,由其他地方转移来的dis会非常大,可以很好的区分
for(int i = 1; i <= dcnt; ++i) if(!in[i]) q1.push(i);
while(!q1.empty()){
dij(q1.front());q1.pop();
}
}
void work()
{
int r, p, s;cin >> n >> r >> p >> s;
for(int i = 1; i <= r; ++i){
int x, y, z;cin >> x >> y >> z;add(x, y, z);add(y, x, z);
}
for(int i = 1; i <= n; ++i) if(!color[i]) ++dcnt, dfs(i);
for(int i = 1; i <= p; ++i){
int x, y, z;cin >> x >> y >> z;add(x, y, z);in[color[y]]++;
}
topsort(s);
for(int i = 1; i <= n; ++i){
if(dis[i] > INF / 2) cout << "NO PATH\n";// 不是由起点s转移的
else cout << dis[i] << endl;
}
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}