zoukankan      html  css  js  c++  java
  • 【模板】dijkstra与floyd

    (我永远喜欢floyd)

    温馨提示:与SPFA一起食用效果更佳

    传送门:https://www.cnblogs.com/Daz-Os0619/p/11388157.html

    Floyd

    大概思路:

    对于某两个点来说,查找他们之间是否连通,若连通,是记录他们间的最短路。

    这里的最短路有两个方向:一个是直接到,一个是通过另外一个点到。(十分单纯的最短路了)

    不需要多讲上代码!

    void floyd() {
        for (int k = 1; k <= n; ++k)
            for (int i = 1; i <= n; ++i)
                for (int j = 1; j <= n; ++j)
                    dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
    }

    Dijkstra

    大概思路:

    (跟SPFA差不多??)

    有一个集合P,一开始只有源点S在P中,每次更新都把集合之外路径值最小的一个放入集合中,更新与它相连的点,重复前面的操作直到集合外没有元素。

    特殊要求:所有边权要是正的

    这时的时间复杂度为O(n*n) (比较危险)

    可以采用堆来优化 时间复杂度O(n*logn)

    (蒟蒻发言:怎么用堆优化?)

     一颗完全二叉树。

    这里用到的小根堆是最小值作为堆顶元素,大根堆反之。

    大概长下面这样:

    利用小根堆优化Dijkstra的理由与用SLF优化SPFA的理由相同,核心思想也相近,但是写法差异很大。

    核心思想:因为每一次都会从队首开始遍历,当队首是最小值时,被更新的所以节点的值也会是最小值,这样可以节省很大一部分时间。

    (这里就体现优先队列的好处)

     下面是核心代码(可以先看注释,后面有图解):

    struct edges {
        int next, to, v;
        edges() {}
        edges(int _n, int _t, int _v) : next(_n), to(_t), v(_v) {}//next表示与这条边同起点的上一条边 
    } e[];
    
    struct heap_node {
        int v, to;
        heap_node() {}
        heap_node(int _v, int _to) : v(_v), to(_to) {}
    };
    inline bool operator < (const heap_node &a, const heap_node &b) {
        return a.v > b.v; //这里实现的是小根堆,如果要大根堆,把 > 改成 < 即可
    }
    
    priority_queue <heap_node> h;
    
    inline void add_to_heap(const int p) {
        for (int x = first[p]; x; x = e[x].next)//first表示以first为头的最后一条边 
            if (dis[e[x].to] == -1)//如果下一条边还没在堆里 
                h.push(heap_node(e[x].v + dis[p], e[x].to));//加上前一个点的最短路值(跟新),放入堆 
    }
    
    void Dijkstra(int S) {
        memset(dis, -1, sizeof(dis));//清空(便于将数据放入堆里) 
        while (!h.empty()) h.pop();//清空堆 
        dis[S] = 0, add_to_heap(S);//现将所有与源点相连的点放入堆中 
        int p;
        while (!h.empty()) { //堆空时所有的点都更新完毕(与SPFA相反) 
            if (dis[h.top().to] != -1)//如果与堆顶元素相连的点已经有了最短路值 
             {
                h.pop();//弹掉堆顶 (此时堆顶不会被更新了) 
                continue;//一直找到可以被更新的点 
            }
            p = h.top().to;//找到与堆顶相连且不在堆里的点 
            dis[p] = h.top().v;//更新最短路值(只是堆顶的) 
            h.pop();//弹掉 
            add_to_heap(p);//再更新与它相连的点,并将与它相连的点放入堆 
        }
    }

    好长的注释啊。。

    再画个图解释一下

    (这里的样例和SPFA的样例一样)

    后面就这样一直循环直到队列为空。为空时所有最短路更新完毕。

    完整代码:

    #include<iostream>
    #include<vector>
    #include<queue>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    struct edge
    {
        int next,to,v;
        edge(){}
        edge(int a,int b,int c)
        {
            next=a;
            to=b;
            v=c;
        }
    }E[10001];
    int first[10001],n,dis[100001];
    int S;
    struct heap
    {
        int to,v;
        heap(){}
        heap(int a,int b)
        {
            to=a;
            v=b;
        }
    };
    inline bool operator< (const heap &a,const heap &b)
    {
        return    a.v>b.v;
    }
    priority_queue<heap> h;
    
    void add_to_heap(int p)
    {
        for(int i=first[p];i;i=E[i].next)
        {
            if(dis[E[i].to]==-1)
            h.push(heap(E[i].to,dis[p]+E[i].v));
        }
    }
    int tot;
    void add_edges(int a,int b,int c)
    {
        E[++tot]=edge(first[a],b,c);
        first[a]=tot;
    }
    void dijkstra(int S)
    {
        while(!h.empty())
        h.pop();
        memset(dis,-1,sizeof(dis));
        dis[S]=0;
        add_to_heap(S);
        int p;
        while(!h.empty())
        {
            if(dis[h.top().to]!=-1)
            {
                h.pop();
                continue;
            }
            p=h.top().to;
            dis[p]=h.top().v;
            h.pop();
            add_to_heap(p);
        }
    }
    int m;
    int main()
    {
        cin>>n>>m>>S;
        int A,B,C;
        for(int i=1;i<=m;i++)
        {
            cin>>A>>B>>C;
            add_edges(A,B,C);
        }
        dijkstra(S);
        for(int i=1;i<=n;i++)
        cout<<dis[i]<<endl;
        return 0;
    }

    结束!

    今天也想放烟花!

  • 相关阅读:
    python编程基础之十四
    python编程基础之十三
    python编程基础之十二
    C++11的新特性
    第六章 分支语句和逻辑运算符
    第五章 循环和关系表达式
    C++ 管理数据内存的方法
    C++数组和指针
    第二、三章 开始学习C++、数据处理
    第一章 预备知识
  • 原文地址:https://www.cnblogs.com/Daz-Os0619/p/11469758.html
Copyright © 2011-2022 走看看