• 详细讲解Codeforces Round #625 (Div. 2) D. Navigation System


     

     

    题意:n个顶点、m条边组成的有向图中,每条边默认距离为1,人要走一条长为k的路径,所经过节点的编号已确定(在p数组中,人从p[0]走到p[k-1])。

        人在走的时候,有一个导航仪,会实时给出 当前位置 c 到目的地 t 的最短路径之一。

        若人下一步到达的节点  与  导航仪给出的下一步节点不同,则导航仪会再重新生成下一步到达节点 与 目的地 t 的最短路径之一。

        因为在存在多条最短路径时,导航仪会随机给出一条最短路径,所以当人按照规定路径走时,导航仪重新生成路径的次数不定。

        求导航仪重新生成路径的最少次数  和  最多次数。

    题解:1. 求导航仪重新生成路径的次数,其实就是求人在走每一步时,是否是沿着当前点到目标点的最短路径,所以需要求其他点到目标点的最短路径或者最短距离。

        显然,求到固定点的最短距离有熟悉的Dijkstra算法、SPFA算法,而求最短路径(获得完整路径所经过的所有点)并不是容易的事情。

        所以我们可以转化为:下一步所走点  到  目标点的距离,是否为  所有从当前点可到达的点中  与目标点距离最小的点。

        n和m都是2*10^5数量级,显然用邻接表(链式前向星)来存储整个图。

        边权值为1,没有负权值,可以直接用堆优化的Dijkstra算法。(不用堆优化是O(n^2),优化后O(mlog(n)) )

       2. 因为Dijkstra算法求的是从源点到其他点的距离,而我们要的是从其他点到终点的距离,所以应该在反图(每条边的方向都置反)中进行。

        而求出所有点到终点的距离数组 d 后,我们需要从起点沿着原来的图检查一遍,看看每一步所到的点  是否为  当前点可到达的所有点中d数组值最小的;

          若是唯一的最小值(即是唯一的最短路径上的点)时,那么导航仪一定不需要Rebuild;

          若是最小值之一(即是最短路径之一上面的点)时,那么导航仪可能要Rebuild,也可能不需要;

          若不是最小值(即不是最短路径上的点)时,那么导航仪一定需要Rebuild。

    详细见代码和注释如下:

    #include<cstdio>    //scanf,printf
    #include<cstring>    //memcpy,memset,strcpy
    #include<vector>    //lower_bound, unique
    #include<cmath>        //log,acos
    #include<algorithm>    //sort
    #include<iostream>    //cin,cout
    #include<map>
    #include<string>    //string
    #include<utility>    //pair
    #include<queue>
    #include<functional>    //greater,less
    using namespace std;
    
    const int maxn = 2e5 + 1;
    struct Graph {
        int head[maxn], nxt[maxn], to[maxn], tot;
        void add_edge(int u, int v) {
            to[++tot] = v;
            nxt[tot] = head[u];
            head[u] = tot;
        }
    }G1, G2;        //需要建正图和反图
    
    int p[maxn], d[maxn];
    bool vis[maxn];
    
    //因为Dijkstra算法是算从源点到其他点的距离,而本题需要求其他点到终点的距离
    void dijkstra(int st) {        //所以在反图上跑,求得 其它点到终点的距离
        memset(d, 0x3f, sizeof d);
        //memset(vis, 0, sizeof vis);    只执行一次时可以省略这行
        priority_queue< pair<int, int>>q;    //默认是大根堆
        q.push({ 0,st });
        d[st] = 0;
        while (!q.empty()) {
            int x = q.top().second;
            q.pop();
            if (vis[x])continue;    //保证只出堆一次,(每个节点只被用来更新一次)
            vis[x] = true;
            for (int i = G2.head[x]; i; i = G2.nxt[i]) {
                int y = G2.to[i];
                if (d[y] > d[x] + 1) {
                    d[y] = d[x] + 1;
                    q.push({ -d[y],y });    //距离的符号变反,就成了距离的小根堆。y可以被push进多次,参与比较
                }
            }
        }
    }
    int main() {
        int n, m, k;
        ios::sync_with_stdio(false); 
        cin >> n >> m;
        for (int i = 0; i < m; i++) {
            int u, v;
            cin >> u >> v;
            G1.add_edge(u, v);            //建立正图
            G2.add_edge(v, u);            //建立反图
        }
        cin >> k;
        for (int i = 0; i < k; i++) {
            cin >> p[i];
        }
        dijkstra(p[k - 1]);    //求出其他点到终点的距离d
    
        int ansmx(0), ansmn(0);
        for (int i = 1; i < k; i++) {    //在正图上走一遍
            int id = p[i];
            int maybe = 2;        //p[i]是唯一的最短路径
            for (int j = G1.head[p[i - 1]]; j; j = G1.nxt[j]) {    //正图上可以获得正确的可走点 信息
                int y = G1.to[j];
                if (y == id)continue;    //记得过滤掉p[i]本身
                if (d[y] < d[id]) {        //p[i]不是最短路径
                    maybe = 0;
                    break;
                }
                else if (d[y] == d[id])maybe = 1;    //p[i]是最短路径之一
            }
    
            if (maybe == 0) {        //p[i]不是最短路径
                ansmx++;
                ansmn++;
            }
            else if (maybe == 1) {    //p[i]是最短路径之一(还有其他最短路径)
                ansmx++;
            }
            //else{ 都不变;}        //maybe = 2,p[i]是唯一的最短路径
        }
        printf("%d %d", ansmn, ansmx);
        return 0;
    }
  • 相关阅读:
    【微积分】 02
    【微积分】 01
    【线性代数】 09
    云南国庆八日游策划书
    Kubectl工具常用命令
    Linux 常用命令缩写及对应的
    kubectl工具的windows安装方法
    Intellij IDEA工具的常用快捷键
    如何理解docker镜像build中的上下文
    【转】在服务器上排除问题的头五分钟&常用命令
  • 原文地址:https://www.cnblogs.com/zsh-notes/p/12416794.html
Copyright © 2020-2023  润新知