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。

猜你喜欢

转载自www.cnblogs.com/ehznehc/p/9880219.html