P3008 [USACO11JAN]道路和飞机Roads and Planes

思路

题中给出的图有单向边和双向边,其中单向边边权可能为负,这其实就说明我们不能直接用dijkstra。(貌似可以双端队列优化的spfa水过去= =

注意到无向边边权是非负的,这提示我们可以在无向边上跑最短路。并且我们可以知道,如果将无向边连接的点缩为一点,最后图中只剩下有向边的话,这个图就是一个DAG,DAG上更新最短路还是很容易的。

所以我们就可以分开考虑,首先求出若干个由无向边组成的连通块,对于块内的点,通过堆优化的dijkstra算法更新最短路;然后在块与块之间,类似于拓扑排序,一层一层地进行更新,最后就可以求出源点\(s\)到所有点的最短路了。

但这里有个细节就是,对于不能到达的点,要删去它们相应的出边,否则拓扑排序就不能正常执行(脑补一下就知道了)。这也就是为什么一开始加入\(s\)所在块时,还要把其它入度为0的块也算进去的原因。(我一开始就是这里卡了好久

还有就是代码中最后判不可到达时,因为题目中路径长度最多为\(25000*10000=2.5*10^8\)。那么能到达的点的最短路值一定小于\(2.5*10^8\)。一开始我们将\(d\)数组置为无穷大(也就是\(2e9\)左右),\(0x3f3f3f3f\)大小为\(1e9\),如果最后的最短路长度大于\(0x3f3f3f3f\),那么一定是不可到的。

详见代码

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 50005, M = 100005;
int n, r, p, s;
struct Edge{
    int v, next, w;
}e[M << 1];
struct node{
    int d, u;
    bool operator < (const node &A) const {
        return d > A.d;
    }
};
int head[N], tot;
void adde(int u, int v, int w) {
    e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;
}
bool vis[N] ;
int cnt;
int bl[N], in[N];
int d[N] ;
vector <int> block[N];
void dfs(int u) {
    vis[u] = 1;
    bl[u] = cnt;
    block[cnt].push_back(u) ;
    for(int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].v;
        if(vis[v]) continue ;
        dfs(v) ;
    }
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> r >> p >> s;
    memset(head, -1, sizeof(head)) ;
    for(int i = 1; i <= r; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        adde(a, b, c);
        adde(b, a, c);
    }
    for(int i = 1; i <= n; i++) {
        if(!vis[i]) {
            cnt++;
            dfs(i) ;
        }
    }
    for(int i = 1; i <= p; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        adde(a, b, c) ;
        in[bl[b]]++;
    }
    memset(vis, 0, sizeof(vis)) ;
    memset(d, 0x7f, sizeof(d)) ;
    d[s] = 0;
    queue <int> Q;
    Q.push(bl[s]) ;
    for(int i = 1; i <= cnt; i++) if(!in[i]) Q.push(i) ;
    priority_queue <node> q;
    while(!Q.empty()) {
        int blo = Q.front();Q.pop() ;
        for(auto u : block[blo])
            q.push(node{d[u], u}) ;
        while(!q.empty()) {
            node now = q.top(); q.pop();
            int u = now.u;
            if(vis[u]) continue ;
            vis[u] = 1;
            for(int i = head[u]; i != -1; i = e[i].next) {
                int v = e[i].v;
                if(d[v] > d[u] + e[i].w) {
                    d[v] = d[u] + e[i].w;
                    if(bl[v] == bl[u]) q.push(node{d[v],v}) ;
                }
                if(bl[v] != bl[u] && (--in[bl[v]]) == 0) Q.push(bl[v]) ;
            }
        }
    }
    for(int i = 1; i <= n; i++) {
        if(d[i] > INF) cout << "NO PATH" << '\n' ;
        else cout << d[i] << '\n' ;
    }
    return 0;
}
上一篇:ssh免密码连接ubuntu


下一篇:模板——AC自动机