• 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;
    }
    
    
  • 相关阅读:
    调用Type.InvokeMember()时出现MissingMethodException
    C#学习之Delegate
    WCF之元数据交换 (Metadata Exchange)
    定义Enum的开始和结束,这样就能循环Enum了
    定制自己的Visual Studio的Debugger Visualizer
    C#中 #if DEBUG 和 Conditional("DEBUG")的区别
    从哪里开始学习Windows 8?(zz)
    Macro 小总结
    WPF应用的图标
    如何把 Visutal studio中的“printonbreakpoint”消息打印在程序的任何地方
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10877531.html
Copyright © 2020-2023  润新知