zoukankan      html  css  js  c++  java
  • dijkstra算法学习笔记

    dijkstra是一种单源最短路径算法,即求一个点到其他点的最短路。不能处理负边权。

    最近某种广为人知的算法频繁被卡,让dijkstra逐渐成为了主流,甚至在初赛中鞭尸了SPFA(?

    dijkstra效率还是不错的,而且不容易被卡。

    一、主要思想

    用dis数组保存最短路径。初始化时,dis[s]设置为0(s为起点),其他无穷大。

    枚举点i,每次从i能够到达的点中,选出权值最小的一个mpl(min place 个人叫法),把dis[mpl]直接设置为权值。可以证明,这就是最短的路径。

    证明:若不是,则必有点k,能使

    s->k+k->mpl < s->mpl

    由权值最小得 s->mpl < s->k 明显矛盾

    (我居然没写易证!

    再枚举mpl到达的点k,若mpl->k+dis[mpl] > dis[k] 则更新dis[k]。专业术语叫做:松弛。

    以此类推,更新完所有的点,算法结束。复杂度O(n2)

    代码:

    #include<iostream>
    #include<vector>
    #include<cstring>
    
    using namespace std;
    
    //GDT TQL Orz
    
    struct edge {
        int next,to,dis;
    } g[500005];//前向星
    
    int dis[500005],gdt[500005],n,m,s,u,v,w,ne=0;
    //gdt就是head数组。此处为玩梗QwQ
    
    inline void add(int from,int to,int d) {
        g[++ne].next=gdt[from];
        g[ne].to=to;
        g[ne].dis=d;
        gdt[from]=ne;
    }//前向星建边
    
    inline void dijkstra() {
        int mn,mpl=s;
        bool vis[500005]= {0};
        dis[s]=0;
        for(register int i=1;i<=n;i++)
        {
            mn=214748364;
            for(register int now=1; now<=n; now++) {
                if(dis[now]<mn&&!vis[now]) {
                    mn=dis[now];
                    mpl=now;
                }
            }
            //选出权值最小的点
            vis[mpl]=1;
    //此节点已被拓展,也就是变为白点(你们可能会从奇奇怪怪
    //的算法书中得到这种说法)。
            for(register int now=gdt[mpl]; now; now=g[now].next) {
                if(!vis[g[now].to]&&dis[g[now].to]>dis[mpl]+g[now].dis)
                    dis[g[now].to]=dis[mpl]+g[now].dis;
            }
    
        }
    }
    
    int main() {
        memset(gdt,0,sizeof(gdt));
        memset(g,0,sizeof(g));
        cin>>n>>m>>s;
        for(int i=1; i<=n; i++)
            dis[i]=2147483647;
        
        for(int i=1; i<=m; i++) {
            cin>>u>>v>>w;
            add(u,v,w);
        }
        dijkstra();
        for(int i=1; i<=n; i++) {
            cout<<dis[i]<<' ';
        }
        return 0;
    }

    二、堆优化

    所谓堆,实际相当于一个优先队列。

    每次把点入队,使用时直接可以访问离s距离最小的。

    具体实现时,会用STL中的priority_queue(优先队列)。而且,一个点需要一个地址(访问)和一个距离s的最短距离(比较、更新最短路径)。

    代码:

    #include<iostream>
    #include<vector>
    #include<cstring>
    #include<queue>
    
    using namespace std;
    
    //GDT TQL Orz
    
    struct edge
    {
        int next,to,dis;
    }g[500005];
    
    struct node
    {
        int k,v;
    //地址key,权值value
    };
    
    bool operator < (node n1,node n2)
    {
        return n1.v>n2.v;
    }
    
    //优先队列维护时需要比较,而结构体无法直接比较,使用重载运算符。
    
    int dis[500005],gdt[500005],n,m,s,u,v,w,ne=0;
    
    inline void add(int from,int to,int d)
    {
        g[++ne].next=gdt[from];
        g[ne].to=to;
        g[ne].dis=d;
        gdt[from]=ne;
    }
    
    inline void dijkstra()
    {
        int mn,mpl=s;
        node nd,temp;
        bool vis[500005]= {0};
        dis[s]=0;
        priority_queue<node> pq;
        temp.k=s;
        temp.v=0;
        pq.push(temp);//第一个点更新起点
        while(!pq.empty())
        {
            nd=pq.top();
            pq.pop();
            
            if(vis[nd.k]) continue;
                vis[nd.k]=1;//拓展
            mpl=nd.k;
            for(register int now=gdt[mpl]; now; now=g[now].next)
            {
                if(!vis[g[now].to]&&dis[g[now].to]>dis[mpl]+g[now].dis)
                {
                    dis[g[now].to]=dis[mpl]+g[now].dis;
                    temp.k=g[now].to;
                    temp.v=dis[g[now].to];
                    pq.push(temp);//新的一个结点
                }
            }
        }
    }
    
    int main() {
        memset(gdt,0,sizeof(gdt));
        memset(g,0,sizeof(g));
        cin>>n>>m>>s;
        for(int i=1; i<=n; i++)
            dis[i]=2147483647;
        
        for(int i=1; i<=m; i++)
        {
            cin>>u>>v>>w;
            add(u,v,w);
        }
        dijkstra();
        for(int i=1; i<=n; i++)
        {
            cout<<dis[i]<<' ';
        }
        return 0;
    }

    三、关于SPFA

    如果一个图没有负边权,那就一定会卡你的SPFA。

  • 相关阅读:
    PHP中GBK和UTF8乱码解决方案
    Ubuntu下的PHP开发环境架设
    Windows 7 IE主页被篡改,如何修复?
    提高代码质量:如何编写函数
    PhpStorm 10 破解方法
    PHP 常用的header头部定义汇总
    kindle 退出演示模式
    好程序与差程序Good Programming, Bad Programming
    如何让你的一天能有26小时?不完全是开玩笑
    毕业若干年,才知道自己原来认为的很多都是错的想法的成熟
  • 原文地址:https://www.cnblogs.com/ehznehc/p/9880219.html
Copyright © 2011-2022 走看看