最短路的几种算法及其优化(模板)

一.Dijkstra 算法

dijkstra算法适用于边权为正的情况,求单源最短路,适用于有向图和无向图

模板伪代码:

清除所有点的标号

设d[0]=0,其余d[i]=INF;

循环n次{

在所有未标记的节点中,寻找d[i]最小的点x

给x做标记

对于从x出发的所有边(x,y)更新d[y]=min(d[y],d[x]+w[x,y]);

}

memset(v,0,sizeof(v));
for(int i=0;i<n;++i)
d[i]=(i==0?0:INF);
for(int i=0;i<n;++i)
{
    int x,m=INF;
    for(int j=0;j<n;++j)
    if(!visit[j]&&d[j]<m)
    {
        m=d[j];
        x=j;
    }
    visit[x]=1;
    for(int j=0;j<n;++j)
    d[j]=min(d[j],d[x]+w[x][j]);
 } 

简单说一下dijkstra的优化:

1.储存结构上:邻接矩阵是很占空间的(众所周知),所以我们一般采用邻接表或者边表

2.堆优化:因为在dijkstra中有循环n次寻找最小dict的过程,我们可以维护一个小根堆来实现,也就把时间复杂度从n^2降到了n*(logn+E)。

优化后的dijkstra,自己写的:

/*
建图用的邻接表,复杂度O(E*logE)
*/

struct pnode {
    int num;
    int len;

    pnode() {}
    pnode(int a, int b) : num(a), len(b) {}//初始化结构体用的,把a复制给num,把b复制给len;
    bool operator < (const pnode tmp) const {
        return len > tmp.len;
    }
};

int dis[N];
bool vis[N];
int n;

void dijkstra(int s) {
    priority_queue<pnode> q;
    q.push(pnode(s, 0));
    pnode u;
    int v, i, res = inf;
    for(i = 0; i <= n; ++i) dis[i] = inf, vis[i] = false;
    dis[s] = 0;

    while(!q.empty()) {
        u = q.top(); q.pop();
        if(u.len != dis[u.num]) continue;/*这是应对优先队列中的重复入队的点,只要最新的那个点就可以了*/
        if(vis[u.num])  continue;
        vis[u.num] = true;

        for(i = head[u.num]; i != -1; i = g[i].next) {
            v = g[i].to;
            if(dis[v] > u.len + g[i].val) {
                dis[v] = u.len + g[i].val;
                q.push(pnode(v, dis[v]));
            }
        }
    }
}

二.Bellman-Ford的优化(也就是SPFA,直接看三吧)

三.SPFA模板及SPFA的优化

1.普通SPFA模板(队列化的Bellman-Ford算法):

int visit[N],dis[N];
bool SPFA(int s)
{
    queue<int>q;
    memset(dis,127,sizeof(dis));
    memset(visit,false,sizeof(visit));    memset(cnt,0s,sizeof(cnt));
    dis[s]=0;
    visit[s]=true;
    q.push(s);
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        visit[k]=false;
        for(int i=head[k];i;i=edge[i].last)/*边表*/
        {
            if(dis[k]+edge[i].w<dis[edge[i].v])
            {
                dis[edge[i].v]=dis[k]+edge[i].w;
                if(!visit[edge[i].v])
                {
                    q.push(edge[i].v);
                    visit[edge[i].v]=true;                    if(++cnt[edge[i].v]>n) /*如果某一个点的入队次数超过了n次,说明存在负环,返回false*/                       return false;
                }
            }
        }
    }
    return true;/*安全跑完了,不存在环*/
}

2.SPFA的优化

SPFA算法有两个优化策略SLF和LLL——SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾; LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出队进行松弛操作。 SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高约 50%。 在实际的应用中SPFA的算法时间效率不是很稳定,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法。实际上dijkstra算法+heap优化后是一定快于一般SPFA的,而且更加稳定。

1)SPFA的SLF优化,(很简单的,只要使用双端队列就可以实现了)。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <deque>
using namespace std;
const int N=501;
const int NN=100001;
const int inf=0x7fffffff;
int n,nu;
typedef struct node
{
    int adj,val;
    struct node *next;
};
node node[NN],*p[N];
int SPFA()
{
    deque<int> qu;
    int x,i,a,b;
    int vis[N],dis[N],num[N];
    struct node *head[N];
    for(i=1;i<=n;i++)
    {
        vis[i]=0;
        num[i]=0;
        dis[i]=inf;
        head[i]=p[i];
    }
    dis[1]=0;
    vis[1]=1;
    num[1]++;
    qu.push_back(1);
    while(!qu.empty())
    {
        x=qu.front();
        qu.pop_front();
        vis[x]=0;
        head[x]=p[x];
        while(head[x])
        {
            a=head[x]->adj;
            b=head[x]->val;
            if(dis[a]>dis[x]+b)
            {
                dis[a]=dis[x]+b;
                if(!vis[a])
                {
                    vis[a]=1;
                    num[a]++;
                    if(num[a]>=n)
                        return 1;
                    if(!qu.empty())
                    {
                        if(dis[a]>dis[qu.front()])
                            qu.push_back(a);
                        else
                            qu.push_front(a);
                    }
                    else
                        qu.push_back(a);
                }
            }
            head[x]=head[x]->next;
        }
    }
    return 0;
}
int main()
{
    int t,i,m,w,a,b,c;
    scanf("%d",&t);
    while(t--)
    {
        memset(node,0,sizeof(node));
        memset(p,0,sizeof(p));
        nu=0;
        scanf("%d%d%d",&n,&m,&w);
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            node[nu].adj=b;
            node[nu].val=c;
            node[nu].next=p[a];
            p[a]=&node[nu];
            nu++;
            node[nu].adj=a;
            node[nu].val=c;
            node[nu].next=p[b];
            p[b]=&node[nu];
            nu++;
        }
        for(i=0;i<w;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            node[nu].adj=b;
            node[nu].val=-c;
            node[nu].next=p[a];
            p[a]=&node[nu];
            nu++;
        }
        if(SPFA())
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}

网上找的

#include<iostream>
using namespace std;
#include<deque>
#include<cstdio>
#include<cstring>
int n,m;
#define N 1001
struct Edge{
    int u,v,w,last;
}edge[N];
int head[N];
int dict[N];
bool visit[N];
void input()
{
    scanf("%d%d",&n,&m);/*n个点,m条边*/
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
        edge[i].last=head[edge[i].u];
        head[edge[i].u]=i;
    }
}
bool SPFA()
{
    deque<int>q;
    memset(dict,127,sizeof(dict));
    memset(visit,false,sizeof(visit));
    dict[1]=0;
    visit[1]=true;;
    int cnt[N];
    memset(cnt,0,sizeof(cnt));/*判断有无环的标志*/
    ++cnt[1];
    while(!q.empty())
    {
        int k=q.front();
        q.pop_front();
        visit[k]=false;/*取出后,不要忘记标志位*/
        for(int l=head[k];l;l=edge[l].last)/*边表*/
        {
            int p=edge[l].v;
            if(dict[p]>dict[k]+edge[l].w)
            {
                dict[p]=dict[k]+edge[l].w;
                ++cnt[p];
                if(cnt[p]>n) return true;/*如果某个点的入队次数超过了n,那么一定存在环*/
                if(!visit[p])
                {
                    visit[p]=true;
                    if(!q.empty())/*这就是SLF Small Label First 策略.的核心,把将要入队的元素的dict与队首元素相比较,如果将要入队的元素的dict大的话,就放在队尾,否则就放在队首 */
                    {
                        if(dict[p]>dict[q.front()])/*这样可以保证始终用dict小的更新,也节约了时间*/
                        q.push_back(p);
                        else q.push_front(p);/*所以必须使用双端队列*/
                    }
                    else q.push_back(p);/*不要忘记考虑队列为空的情况*/
                }
            }
        }
    }
    return false;
}
int main()
{
    input();
    if(SPFA())
    printf("circle");
    else{
        for(int i=1;i<=n;++i)
        printf("%d ",dict[i]);
    }
    return 0;
}

自己打了一遍,还可以理解

2.SPFA的LLL优化

在网上实在没找到可靠的。

3.SPFA的DFS优化:

在很多的题目中,SPFA都是用BFS来实现的,对于随机图来说,BFS的速度会远大于DFS,但是对于某些特殊的图结构来说,DFS也是一个很好的选择

例如 1):题目中要求在最短的时间内,判断有无环,DFS明显会比BFS快(例题是POj上一个判断单词接龙的题目)

2):对于网格型的图,DFS的速度比BFS快

模板:

void SPFA(int k)
{
    flag[k]=true;
    for(int l=head[k];l;l=edge[l].last)
    {
        int v=edge[l].v;/*找出第一个可以被更新的点*/
        if(dis[v]>dis[k]+edge[l].w)
        {
            dis[v]=dis[k]+edge[l].w;
            if(!flag[v])
            {
                SPFA(v);/*接着深搜下去*/
            }
            else /*这表明从某个点开始DFS,结果又搜到了这个点,说明存在负权回路*/
            {
                printf("cycle");
                return;
            }
        }
    }
    flag[k]=false;/*不要忘记再把k设为false,为的是让他能够重复入队*/
}

四,Floyd算法模板(没有任何优化,就是邻接矩阵和n^3,一般情况单源最短路是绝对不会用的)

for(int k=1;k<=n;++k)/*注意这个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]); 
时间: 2024-08-05 09:17:46

最短路的几种算法及其优化(模板)的相关文章

求解最短路的四个算法及其优化

目录 求解最短路的四个算法及其优化 1.Dijkstra算法 <1.朴素Dijkstra算法: <2:堆优化的Dijkstra算法 2.Floyd算法 3.Bellman-Ford算法 4.SPFA算法 求解最短路的四个算法及其优化 1.Dijkstra算法 Dijkstra很好的运用了贪心算法,其思想是一直找离已加入顶点集合的最短边,更新邻点,下面是实现代码: <1.朴素Dijkstra算法: [题意]:给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为正值.请你求出1

几种常见的优化算法

我们每个人都会在我们的生活或者工作中遇到各种各样的最优化问题,比如每个企业和个人都要考虑的一个问题"在一定成本下,如何使利润最大化"等.最优化方法是一种数学方法,它是研究在给定约束之下如何寻求某些因素(的量),以使某一(或某些)指标达到最优的一些学科的总称.随着学习的深入,博主越来越发现最优化方法的重要性,学习和工作中遇到的大多问题都可以建模成一种最优化模型进行求解,比如我们现在学习的机器学习算法,大部分的机器学习算法的本质都是建立优化模型,通过最优化方法对目标函数(或损失函数)进行优

关于SPFA算法的优化方式

关于SPFA算法的优化方式 这篇随笔讲解信息学奥林匹克竞赛中图论部分的求最短路算法SPFA的两种优化方式.学习这两种优化算法需要有SPFA朴素算法的学习经验.在本随笔中SPFA朴素算法的相关知识将不予赘述. 上课! No.1 SLF优化(Small Label First) 顾名思义,这种优化采用的方式是把较小元素提前. 就像dijkstra算法的堆优化一样.我们在求解最短路算法的时候是采取对图的遍历,每次求最小边的一个过程,为了寻找最小边,我们需要枚举每一条出边,如果我们一上来就找到这个边,那

A*寻路算法的优化与改进

提要 通过对上一篇A*寻路算法的学习,我们对A*寻路应该有一定的了解了,但实际应用中,需要对算法进行一些改进和优化. Iterative Deepening Depth-first search- 迭代深化深度优先搜索 在深度优先搜索中一个比较坑爹情形就是在搜索树的一枝上没有要搜的结果,但是却非常深,甚至深不见底,这样就根本搜索不到结果.为了防止这种情况出现,就出现了Iterative Deepening的思想. 迭代深化搜索(Iterative deepening search, IDS)或者

关于最短路的几个算法

关于最短路的几个算法有Dijkstra,Bellman-Ford,Floyd Dijkstra: Dijkstra适用于边权为正的情况,从单个源点出发,到其他所有结点的最短路 算法的核心是用已经知道的结点 i 的距离 d[i] 去更新和这个结点相连的其他结点的距离 void Dijkstra() { memset(vis,0,sizeof(vis)); //vis数组表示结点是否被访问 memset(d,INF,sizeof(d)); //d数组表示到结点的距离 d[s]=0; //将起点的距离

字符串匹配的三种算法

下面将介绍三种有关字符串匹配的算法,一种是朴素的匹配算法,时间复杂度为O(mn),也就是暴力求解.这种方法比较简单,容易实现.一种是KMP算法,时间复杂度为O(m+n),该算法的主要任务是求模式串的next数组.另外还有一种对KMP算法的改进,主要是求nextval数组. 第一种朴素的匹配算法: int index(char str[], char subStr[]) { int i = 0, j = 0,index = 0; while (str[i] != '\0' && subStr

谈谈&quot;求线段交点&quot;的几种算法(js实现,完整版)

"求线段交点"是一种非常基础的几何计算, 在很多游戏中都会被使用到. 下面我就现学现卖的把最近才学会的一些"求线段交点"的算法总结一下, 希望对大家有所帮助. 本文讲的内容都很初级, 主要是面向和我一样的初学者, 所以请各位算法帝们轻拍啊 嘎嘎 引用 已知线段1(a,b) 和线段2(c,d) ,其中a b c d为端点, 求线段交点p .(平行或共线视作不相交) =============================== 算法一: 求两条线段所在直线的交点, 再

求线段交点&quot;的几种算法(js实现,完整版)

"求线段交点"是一种非常基础的几何计算, 在很多游戏中都会被使用到. 下面我就现学现卖的把最近才学会的一些"求线段交点"的算法说一说, 希望对大家有所帮助. 本文讲的内容都很初级, 主要是面向和我一样的初学者, 所以请各位算法帝们轻拍啊 嘎嘎 引用 已知线段1(a,b) 和线段2(c,d) ,其中a b c d为端点, 求线段交点p .(平行或共线视作不相交) 算法一: 求两条线段所在直线的交点, 再判断交点是否在两条线段上. 求直线交点时 我们可通过直线的一般方程

素数算法的优化之路

一.素数的定义 质数又称素数.指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数(不包括0)整除的数.因为合数是由若干个质数相乘而得来的,所以,没有质数就没有合数,由此可见质数在数论中有着很重要的地位. 比如:2,3,5,7,9.....都是素数. 二.构造素数算法 写算法之前,先来说说以下这个东西: 对于任意一个合数n,如果它有两个质因子x,y,显然n = x*y, 所以,由不等式性质可得,x <= sqrt(n), 即 x <= n^(1/2). 推广一下,对于任意一个合数,