zoukankan      html  css  js  c++  java
  • 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" << '
    ' ;
            else cout << d[i] << '
    ' ;
        }
        return 0;
    }
    
    
  • 相关阅读:
    操作系统复习
    软件工程复习
    2020字节跳动校园招聘算法方向第二场考试题解(部分)
    【牛客】用两个栈来实现一个队列
    LeetCode 62. 不同路径
    LeetCode 79. 单词搜索
    LeetCode 113. 路径总和 II
    LeetCode 389. 找不同
    【牛客】矩阵交换
    【牛客】KiKi学习了结构体和指针
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10877531.html
Copyright © 2011-2022 走看看