• 题解 P1186 【玛丽卡】


    见大佬们都是用的SPFA

    为了关心Dijkstra党,所以这里写一篇关于Dijkstra的题解

    主要思路:

    这个题意实在是太模糊了,我也是听了大佬的解释才明白的。

    我可以再次翻译一遍题意QAQ:

    找出在一条边断掉的情况下的单源最短路径,并输出最坏的情况(去边不同会影响最短路)。

    完了???

    完了。。。

    所以思路就出来了:一一去掉所有边,分别去求最短路。

    仔细想想:时间复杂度(Dijkstra)O(n^2*m)

    如果是dalao写带堆优化的也会到 O(nlogn*m)

    代码如下(提示:这个只有80分哦,至于为什么,看复杂度啊)

    代码1:

    #include <algorithm>
    #include <cmath>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <ctime>
    #include <iostream>
    #include <map>
    #include <queue>
    #include <set>
    #include <stack>
    #include <string>
    #include <vector>
    using namespace std;
    #define go(i, j, n, k) for (int i = j; i <= n; i += k)
    #define fo(i, j, n, k) for (int i = j; i >= n; i -= k)
    #define rep(i, x) for (int i = h[x]; i; i = e[i].nxt)
    #define mn 1010
    #define inf 1 << 30
    #define ll long long
    #define ld long double
    #define fi first
    #define se second
    #define root 1, n, 1
    #define lson l, m, rt << 1
    #define rson m + 1, r, rt << 1 | 1
    #define bson l, r, rt
    inline int read(){
        int f = 1, x = 0;char ch = getchar();
        while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
        while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    inline void write(int x){
        if (x < 0)putchar('-'),x = -x;
        if (x > 9)write(x / 10);
        putchar(x % 10 + '0');
    }
    //This is AC head above...
    struct edge{
        int v,nxt,w;
    } e[(mn * (mn-1)) >>1];
    int h[mn],p;
    inline void add(int a,int b,int c){
        p++;
        e[p].nxt=h[a];
        h[a]=p;
        e[p].v = b;
        e[p].w = c;
    }
    //Dij + 堆优化
    struct node{
        int u,v;
        bool operator <(const node &b) const{
            return u > b.u;
        }
    };//重载运算符
    int n,m,s,pp;
    int dis[mn];//起点到每个点的距离
    int pre[mn], per[mn];
    inline void Dij(){//s为起点
        int s = 1;
        priority_queue<node> q;//优先队列
        go(i, 0, n, 1)
            dis[i] = inf;
        dis[s] = 0;
        node o;
        o.u = 0;
        o.v = s;
        q.push(o);
        while (q.size()){
            int u = q.top().v;
            int d = q.top().u;
            q.pop();
            if(d!=dis[u])//玄学的优化不解释
                continue;//据说有这个优化可以用Dijkstra+堆优化过P4779
            rep(i,u){
                int v = e[i].v;
                int w = e[i].w;
                if (dis[v] > dis[u] + w){
                    dis[v] = dis[u] + w;
                    node ppp;
                    ppp.u = dis[v], ppp.v = v;
                    q.push(ppp);
                }
            }
        }
    }
    int maxx=-1;
    int main(){
        n = read(), m = read();
        go(i,1,m,1){
            int x = read(), y = read(), z = read();
            add(x, y, z);
            add(y, x, z);
        }
        go(i,1,m*2,1){//每一条边(注意是2*m)删掉,跑一边最短路,求之中最大值
            int r = e[i].w;
            e[i].w = inf;
            Dij();
            //cout << dis[n] << " ";
            maxx = max(maxx, dis[n]);
            e[i].w = r;//类似回溯的东东
        }
        //cout << "
    ";
        cout << maxx;
        return 0;
    }
    
    

    不过时间复杂度太高了!!!怎么办???

    不慌,慌也没用

    我们不是分析过题吗?去掉一条边的单源最短路。如果去的边不是不删边情况下的单源最短路径上的边,那么会对最终答案有影响吗?

    没有!

    所以,我们只需要看一看单源最短路上的边分别去掉会是什么情况就可以了。正常操作是先跑一遍Dijkstra(最短路),然后再去记录单源最短路上经过的边,把路过的边分别去掉,分别跑一边单源最短路取最大值就好啦!

    所以,代码如下:(在记录路过的边和删边上我会在注释里解释的QwQ)

    代码2:

    #include <algorithm>
    #include <cmath>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <ctime>
    #include <iostream>
    #include <map>
    #include <queue>
    #include <set>
    #include <stack>
    #include <string>
    #include <vector>
    using namespace std;
    #define go(i, j, n, k) for (int i = j; i <= n; i += k)
    #define fo(i, j, n, k) for (int i = j; i >= n; i -= k)
    #define rep(i, x) for (int i = h[x]; i; i = e[i].nxt)
    #define mn 1010
    #define inf 1 << 30
    #define ll long long
    #define ld long double
    #define fi first
    #define se second
    #define root 1, n, 1
    #define lson l, m, rt << 1
    #define rson m + 1, r, rt << 1 | 1
    #define bson l, r, rt
    inline int read(){
        int f = 1, x = 0;char ch = getchar();
        while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
        while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    inline void write(int x){
        if (x < 0)putchar('-'),x = -x;
        if (x > 9)write(x / 10);
        putchar(x % 10 + '0');
    }
    //This is AC head above...
    struct edge{
        int f,v,nxt,w;
    } e[(mn * (mn-1)) >>1];//这个地方注意边的最大条数
    int h[mn],p;
    inline void add(int a,int b,int c){
        p++;
        e[p].nxt=h[a];
        h[a]=p;
        e[p].v = b;
        e[p].w = c;
        e[p].f = a;//小伙伴看这里!这个地方又记录了一条边的起点,便
        		   //于一条边从后往前找
    }
    //Dij + 堆优化
    struct node{
        int u,v;
        bool operator <(const node &b) const{
            return u > b.u;
        }
    };//重载运算符
    int n,m,s,pp;
    int dis[mn];//起点到每个点的距离
    int pre[mn], per[mn];
    inline void Dij(bool zzz){//s为起点
        int s = 1;
        priority_queue<node> q;//优先队列
        go(i, 0, n, 1)
            dis[i] = inf;
        dis[s] = 0;
        node o;
        o.u = 0;
        o.v = s;
        q.push(o);
        while (q.size()){
            int u = q.top().v;
            int d = q.top().u;
            q.pop();
            if(d!=dis[u])
                continue;
            rep(i,u){
                int v = e[i].v;
                int w = e[i].w;
                if (dis[v] > dis[u] + w){
                    dis[v] = dis[u] + w;
                    if(zzz){//这个地方是用来判断是第几遍最短路的,如
                            //果是一开始没有删边的最短路,就记录最短路路径,如
                            //果是删过边以后,就不能更新这个per数组了
                        pre[e[i].f] = v;//这句好像没啥用,忽略了吧
                        per[e[i].v] = i;//重点在这里!这里per数组记
                                        //录的是到某个点中最短路径上的边,这样便于找他的入边
                    }
                    node ppp;
                    ppp.u = dis[v], ppp.v = v;
                    q.push(ppp);
                }
            }
        }
    }
    int maxx=-1;
    int main(){
        n = read(), m = read();
        go(i,1,m,1){
            int x = read(), y = read(), z = read();
            add(x, y, z);
            add(y, x, z);
        }
        Dij(true);//没有删边的最短路(应同学们需求,我写的Dijkstra)
        //go(i,1,n,1){
        //    cout << dis[i] << " ";
        //}
        //cout << "
    ";
        pp = n;
        while(pp!=1){
            int r = e[per[pp]].w;//为了删边重建用
            e[per[pp]].w = inf;//删边就是把边权改到(认为)无穷大,这样在跑最短路
                               //时就肯定不会用这条边了
            Dij(false);//删边后的最短路
            //cout << dis[n] << " ";
            maxx = max(maxx, dis[n]);//求最大
            e[per[pp]].w = r;//之前删过的边一定要再加回来,要不影响下一次的最短
            				 //路计算
            pp = e[per[pp]].f;//寻找前面一条边
        }
        //cout << "
    ";
        cout << maxx;
        return 0;
    }
    
    

    第七次写题解,希望可以帮到写Dijkstra但被惨遭TLE而80分的同学,有错误请dalao们多多指教

    NOIP2018并不是结束,而是开始
  • 相关阅读:
    Redis-配置文件与持久化
    Redis-搭建简单集群
    python实现简单的统计
    Redis-三种数据结构与事务
    String 类的理解
    针对Java面试来学习JAVA内容,让你事半功倍
    jdbc实现原理
    一朋友的Java基础面试题及答案(TCP/IP部分)
    Java Docker容器化技术详解
    Java新手开源项目集合
  • 原文地址:https://www.cnblogs.com/yizimi/p/10056245.html
Copyright © 2020-2023  润新知