ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

模板 --单源最短路

2020-09-18 19:35:41  阅读:257  来源: 互联网

标签:dij vis -- 单源 pos int 500001 include 模板


模板 --单源最短路

求最短路一般有两种方法,dij,SPFA;

大多数情况下最常用并且最稳妥的就是dij,SPFA一般用于判断负权值和负环,并且如果边较多,SPFA容易被卡死。所以一般情况下都是使用dij。

首先先介绍dij

dij算法的主要思想是先寻找一个点A1,将这个点并入一个集合,然后找出与这个点相连的点中路径最小的点A2,然后将A2并入集合,将与A2相连点A3的路径加上A1到A2点的路径与A1到A3的路径比较,如果前者小于后者,就将A1到A3路径更新。

以此类推,直到将所有点加入集合中。我们用一个数组储存点A到所有点的最短路径。

图的存储也有多种方式,例如二维数组储存(邻接矩阵),C++中STL里的vector储存,还有链式前向星(邻接表)。其中用二维数组储存只能使用与数据范围较小的题,如果数据范围超过1e5,基本都是使用vector和链式前向星储存。

下面先放一个二维数组储存的代码模板;

void Dij(int s) // s为起点
{
    memset(vis, 0, sizeof(vis));
    dis[s]=0;
    for(int i = 1; i <= n; i++)dis[i] = INF;//INF为一个很大的数
    int v,mn;
    for(int i = 1; i <= n; i++)
    {
        mn = INF;
        for(int j = 1; j <= n; j++)//找最小值的点
        {
            if(!vis[j] && dis[j] < mn)
            {
                mn = dis[j];
                v = j;
            }
        }
        vis[v] = 1;
        for(int j = 1; j <= n; j++)
        {
            if(!vis[j] && (mn + mp[v][j]) < dis[j])dis[j] = mn + mp[v][j];
        }
    }
}

一般情况下不推荐使用邻接矩阵存图,很容易TLE;

下面是使用C++STL中的vector存图;

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<algorithm>
#include<vector>
#include<map>
#include<cmath>
#define long long ll;
using namespace std;
const int MAXN= 0x3f3f3f3f;
const int INF =2147483647;
struct node{
    int v;
    int w;
};//创建一个结构体,到达点和距离。
node make_out(int x,int y)
{
    node t;
    t.v = x;
    t.w = y;
    return t;
//将到达点和距离存入vector。
}
vector<node> q[500001];
int n, m, s;
int d[500001];
int vis[500001];
void dij(int x)
{
        memset(vis, 0, sizeof(vis));
         for (int i = 1; i <= n;i++)
       { d[i] = INF;}//初始化赋值为最大值
    for (int i=0; i < q[x].size();i++)
    {
        int h = q[x][i].v;
        d[h] = q[x][i].w;
    }//更新与X到与X相邻点的距离
    d[x] = 0;//到本身的距离为0
    int pos;
    for (int i = 1; i < n; i++)//寻找路程最小的点
    {
        int mm = INF;
        for (int j = 1; j <= n; j++)
        {
            if (!vis[j] && mm > d[j])
            {
                mm = d[j];
                pos = j;
            }
        }
        vis[pos] = 1;
        for (int k = 0; k < q[pos].size(); k++)//松弛操作
        {
            int h = q[pos][k].v;
            if (d[h]>q[pos][k].w +d[pos])
            {
                d[h] = q[pos][k].w +d[pos];
            }
        }
    }
}
int main()
{
    cin >> n >> m >> s;
        for (int i = 1; i <= m; i++)
        {
            int x, y, z;
            cin >> x >> y >> z;
            q[x].push_back(make_out(y, z));
        }
        dij(s);
        for (int i = 1; i <= n;i++)
        {
    
            cout << d[i]<<' ';
        }
        return 0;
}





然后就是使用链式前向星,跟Vector一样都是邻接表储存方式。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<algorithm>
#include<vector>
#include<map>
#include<cmath>
using namespace std;
const int INF= 0x3f3f3f3f;
const int MAXN = pow(2, 31) - 1;
int head[500001];//head是储存最新起始点的位置
int ans = 0;
queue<int> q;
struct edge
{
    int u, v, w, next;//next是储存上一条边的位置
}e[500001];
int n, m;
void add(int u,int v,int w)//链式前向星存图
{
    e[++ans].next = head[u];
    e[ans].v = v;
    e[ans].w = w;
    head[u] = ans;
}
int d[500001];
int vis[500001];
void DIJ(int x)
{
    memset(vis, 0, sizeof(vis));//跟vector类似
    for (int i = 1; i <= n;i++)
    {
        d[i] = MAXN;
    }
        d[x] = 0;

        int pos = x;
        while (!vis[pos])//这里的意思是是否访问所有点的
            {for (int i = head[pos]; i != 0;i=e[i].next)
            {
                int h = e[i].v;//松弛操作
                if(d[h]>d[pos]+e[i].w)
                {
                    d[h] = d[pos] + e[i].w;
                }
            }
            vis[pos] = 1;
            int mn = MAXN;
            for (int j = 1; j <= n; j++)//寻找最大值的点
            {
                if (!vis[j] && mn > d[j])
                {
                    mn = d[j];
                    pos = j;
                }

                }
                
            }
}
int main()
{
    int s;
    cin >> n >> m>>s;
    for (int i = 1; i <= m;i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        add(x, y, z);
    }
   DIJ(s);
    for (int i = 1; i <= n;i++)
    {
        cout << d[i] << " ";
    }
        return 0;
}



以上三种方式都是使用普通的dij,但是有时候会遇到一些数据很大的,dij还是可以优化的,最常用的就是dij的堆优化。

所谓的堆优化就是使用C++STL中的优先队列来维护dij,dij中有一步是寻找距离最小的点。使用堆优化就可以减少时间复杂度。

下面放堆优化模板

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
const int MAXN= 0x3f3f3f3f;
struct node{
    int u;
    int s2;
    bool operator <(const node& r)const//重载优先队列,最小堆
    {
        return r.s2 < s2;
    }
};
struct MS
{
    int u, v, w, next;
}e[200001];
int head[200001];
int ans = 0;
void add(int u,int v,int w)//链式前向星
{
    e[++ans].next = head[u];
    e[ans].v = v;
    e[ans].w = w;
    head[u] = ans;
}
priority_queue<node> q;//优先队列
int d[200001];
int n, m,s;
void dij()
{
    for (int i = 1; i <= n;i++)
       { d[i] = 2147483647;}
        d[s] = 0;
    q.push((node){s, 0});//将距离和点放入优先队列中
while(!q.empty())
{
    node t = q.top();//取堆顶元素,距离一定是最小的
    q.pop();
    int u = t.u;
    int s1 = t.s2;
     if(s1!=d[u])//这步是堆优化的基本操作,防止没更新的进入循环,减少时间复杂度
            continue;
    for (int i = head[u]; i != 0;i=e[i].next)
    {int v1 = e[i].v;
        if (d[v1]>d[u]+e[i].w)//链式前向星基本遍历操作,松弛操作
        {
            d[v1] = d[u] + e[i].w;
            q.push((node){v1, d[v1]});//入队
        }
    }
}
}
int main()
{
    cin >> n >> m >> s;
    for (int i = 1; i <= m;i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        add(x, y, z);
    }
    dij();
    for (int i = 1; i <= n;i++)
       { cout << d[i]<<' ';}
        return 0;
}





dij算法差不多结束了,接下来就是SPFA.

SPFA也是一种求单源最短路的算法。

其实它的思路跟dij差不多,但是不同的是dij的入集合的点是不会再变动,而SPFA中集合中的点一直有变动,并且SPFA是使用队列来维护,已经入集合的点也是有可能出来。

下面放代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<algorithm>
#include<vector>
#include<map>
#include<cmath>
using namespace std;
const int INF= 0x3f3f3f3f;
const int MAXN = pow(2, 31) - 1;
struct node{
    int u, v, w, next;
}e[500001];
int head[500001];
int ans = 0;
void add(int u,int v,int w)//链式前向星
{
    e[++ans].next = head[u];
    e[ans].v = v;
    e[ans].w = w;
    head[u] = ans;
}
int n, m, s;
queue<int> q;
int vis[500001];
int d[500001];
void SPFA(int x)
{
    for (int i = 1; i <= n;i++)
        d[i] = MAXN;
    d[x] = 0;
    q.push(x);
    vis[x] = 1;//入队标记为1
    while(!q.empty())
    {
        int h = q.front();
        q.pop();
        vis[h] = 0;//出队标记为0
        for (int i = head[h]; i!=0;i=e[i].next)
        {
            int t = e[i].v;
           if(d[t]>d[h]+e[i].w)//松弛操作
               { d[t] = d[h] + e[i].w;
                if(!vis[t])//如果队里不存在相同的点,则入队。
                {
                    q.push(t);
                }}
        }
    }
}
int main()
{
    cin >> n >> m >> s;
    memset(vis, 0, sizeof(vis));
    memset(d, 0, sizeof(d));
    for (int i = 1; i <= m;i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        add(x, y, z);
    }
    SPFA(s);
    for (int i = 1; i <= n;i++)
    {
        cout << d[i] << ' ';
    }
        return 0;
}

标签:dij,vis,--,单源,pos,int,500001,include,模板
来源: https://www.cnblogs.com/a821403286/p/13693036.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有