zoukankan      html  css  js  c++  java
  • BZOJ 2200 道路与航线

    题意

    有N个点的图,M条双向边,K条单向边,只有单向边可能为负,并且含有单向边的路径不是回路,求从S出发到所有点的最短路径长度

    分析

    由于有负权边,不能直接上spfa。相信小伙伴们一定看过了《算法竞赛进阶指南》,所以与书上重复的就不再讲了,只罗列一些我自己思考到的东西

    • 如果按照拓扑序,一定会有一个连通块入度为0吗?答案是一定会有的,如果没有,那么肯定有一条含有单向边的路是回路。
    • s点所在连通块入度不为0会怎么样?其实并不会怎么样,说明那些入度为0的连通块中的点都不能从s点出发到达。所以直接无脑按照拓扑序去扫就行了。在进一步讲,在存储拓扑序的队列中,跟s所在连通块深度相同的连通块中的点都是无法到达的。
    • 做法思路主要切入点是只有单向边为负,而且不构成环,所以就把单向边所连的连通块看成一个点的话,整个图就是个DAG,DAG的最短路直接通过求拓扑序来求即可。
    • 另外要注意一个细节是,每次处理一个连通块时,都是先把该连通块的所有点都放到优先队列中(想一下为什么要这么做)
    #include <bits/stdc++.h>
    using namespace std;
    const int N = 25000;
    const int M = 200010;
    const int inf = 0x3f3f3f3f;
    vector<int> vec[N];
    int fa[N],head[N],ver[M],nxt[M],edge[M],tot;
    int d[N],v[N],deg[N];
    int x,y,z,n,m,k,s;
    queue<int> q;// 拓扑序
    priority_queue<pair<int,int> > pq;// 单个连通块dij
    void add(int x,int y,int z){
        ver[++tot] = y;edge[tot] = z;nxt[tot] = head[x];head[x] = tot;
    }
    int find(int x){
        return x == fa[x] ? x : fa[x] = find(fa[x]);
    }
    void dij(int mark){
        for(int i=0;i<vec[mark].size();i++){
            int x = vec[mark][i];
            pq.push({-d[x],x});
        }
        while(pq.size()){
            int x = pq.top().second;pq.pop();
            if(v[x])continue;
            v[x] = 1;
            for(int i=head[x];i;i=nxt[i]){
                int y = ver[i];
                // y 和 x 不在同一个连通块
                if(find(y) != find(x)){
                    d[y] = min(d[y],d[x] + edge[i]);
                    if(--deg[find(y)] == 0){
                        q.push(find(y));
                    }
                }else{// 在同一个连通块
                    if(d[y] > d[x] + edge[i]){
                        d[y] = d[x] + edge[i];
                        pq.push({-d[y],y});
                    }
                }
            }
        }
    }
    int main(){
        scanf("%d%d%d%d",&n,&m,&k,&s);
        for(int i=1;i<=n;i++)fa[i] = i,d[i] = inf;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,z);
            add(y,x,z);
            x = find(x);
            y = find(y);
            fa[x] = y;
        }
        for(int i=1;i<=n;i++){
            int f = find(i);
            vec[f].push_back(i); // f 联通块中有1号点
        }
        for(int i=1;i<=k;i++){
            scanf("%d%d%d",&x,&y,&z);// x -> y 有一个 航道
            add(x,y,z);
            x = find(x);
            y = find(y);
            deg[y] ++;
        }
        // s 所在连通块入度不为 0 那么...就要消灭掉之前的这些连通块,都是无法到达的
        for(int i=1;i<=n;i++){
            if(deg[i] == 0 && vec[i].size()){
                q.push(i);
            }
        }
        d[s] = 0;
        while(q.size()){
            int x = q.front();//标号为x的连通块
            q.pop();
            dij(x);// 处理x连通块
        }
        for(int i=1;i<=n;i++){
            if(d[i] >= 1e9){
                puts("NO PATH");
            }else printf("%d
    ",d[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    sqlhelper使用指南
    大三学长带我学习JAVA。作业1. 第1讲.Java.SE入门、JDK的下载与安装、第一个Java程序、Java程序的编译与执行 大三学长带我学习JAVA。作业1.
    pku1201 Intervals
    hdu 1364 king
    pku 3268 Silver Cow Party
    pku 3169 Layout
    hdu 2680 Choose the best route
    hdu 2983
    pku 1716 Integer Intervals
    pku 2387 Til the Cows Come Home
  • 原文地址:https://www.cnblogs.com/1625--H/p/11564463.html
Copyright © 2011-2022 走看看