最短路笔记(简单过过)

这个比较简单,但是还是要复习一下的

目录

  1. 什么是图
  2. 最短路算法
  3. 各个算法的复杂度
  4. 相关题目

1.0什么是图

1.1图的定义

图(Graph):是由一种由顶点(Vertex)非空有限集合和顶点之间边(Edge)的集合组成的数据结构,表示为G(V,E)。

其中,G为图,V为顶点集合,E为边的集合。顶点集合有穷非空,边集合可以为空。

1.2图的分类

简单来说也就两种:有向图和无向图。

2.0最短路算法

2.1 Floyd算法

时间复杂度 O(n3)。

这个算法其实就是一个大暴力,

在题目数据范围 ≤500的情况下,建议使用。(毕竟代码量少啊)

算法思路

对于两个点i,j,两个点的最短路为disij

从1到n枚举一个k,

如果disik+diskj<disij (即让k为一个中间商一样的东西)

那我们就让disij更新。

代码实现

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]);
   }
 }
}

2.2 DijkStra算法

时间复杂度:O(n2)(可优化到O(n log n))

可以说是最常用的了,因为优化后很稳定。不像SPFA

注意:有负权值时不得使用(SPFA上场了

算法思路

每次找到离源点最近的一个顶点,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径。

借用了AW顿顿的一幅图,别介意qwq

举个例子

求A到E的最短距离。

答案是:11

个人感觉这种手算用spfa不容易错,但dij更容易些吧。

过程:

  1. 一开始,disB=3,disF=6,disG=4
  2. 找A相邻的最短边且没遍历过的边,即B,那disC=4,disD=10。
  3. 找B相邻的最短边且没遍历过的边,即C,那disE=12,disF=5。
  4. 找C相邻的最短边且没遍历过的边,即F,那disE=11,disD=7。

(由于作者懒,已经推出了正确路径后,后面的模拟就懒得做了,于是不写了)

代码实现

普通版本

#include<bits/stdc++.h>
#define MAXN 0x7fffffff
using namespace std;
int dist[5001],n,m,edge[5001][5001],minn,idx,s;
bool vis[5001];
void dijkstra(){
    for(int j=1;j<n;++j){
        minn=MAXN;
        for(int i=1;i<=n;i++){
            if(!vis[i]&&dist[i]<minn){
                minn=dist[i];
                idx=i;
            }
        }
        vis[idx]=1;
        for(int i=1;i<=n;i++){
            if(edge[idx][i]!=MAXN){
                int t=edge[idx][i]+dist[idx];
                if(dist[i]>t)dist[i]=t;
            }
        }
    }
}   
int main()
{
    cin>>n>>m>>s;
    for(int i=1;i<=n;i++)for(int j=1;j<=n;++j){
        if(i!=j)edge[i][j]=MAXN;
    }
    for(int i=0;i<m;i++){
        int x,y,len;
        cin>>x>>y>>len;
        edge[x][y]=len;
    }
    for(int i=1;i<=n;++i){
        dist[i]=edge[s][i];
    }
    vis[s]=1;
    dijkstra();
    for(int i=1;i<=n;i++)cout<<dist[i]<<' ';
    return 0;
}

优化版本(用了简单的优先队列)

#include<bits/stdc++.h>
using namespace std;
#define maxn 100050
#define maxm 500005
#define INF  1234567890

struct Edge
{
    int u,v,w,next;
}e[maxm];

int head[maxn],cnt,n,m,s,vis[maxn],dis[maxn];

struct node
{
    int w,now;
    inline bool operator <(const node &x)const

    {
        return w>x.w;
    }
};
priority_queue<node>q;

inline void add(int u,int v,int w)
{
    e[++cnt].u=u;
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt;
}

void dijkstra()
{
    for(int i=1;i<=n;i++)
    {
        dis[i]=INF;
    }
    dis[s]=0;
    q.push((node){0,s});
    while(!q.empty())
    {
        node x=q.top();
        q.pop();
        int u=x.now;
        if(vis[u]) continue; 
        vis[u]=1;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].v;
            if(dis[v]>dis[u]+e[i].w)
            {
                dis[v]=dis[u]+e[i].w;
                q.push((node){dis[v],v});
            }
        }
    }
}
int main()
{
   cin>>n>>m>>s;

    for(int i=1,x,y,z;i<=m;i++)
    {
        cin>>x>>y>>z;
        add(x,y,z);
    }
    dijkstra();
    for(int i=1;i<=n;i++)
    {
        printf("%d ",dis[i]);
    }
    return 0;
}

2.3 SPFA算法

它死了

时间复杂度:O(nm)(最坏情况,即菊花图)

但它可以实现有负权值的情况。

算法思路

大概思路就是从起点出发,每个点(除了起点)到起点的距离设为inf

此时将起点入队(queue)

用while语句当队列不为空时

每次提取队头元素(x)

找到队头相邻的点(y)

接着作比较if(disx+exy<disy)(eij表示i到j的边长)

如果disy发生了改变,

并且y不在队列,那就把y放入队列。

代码实现

#include<iostream>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define INF 999999999

using namespace std;

const int N=4e6+50;

int n,m,x,y,tot,f[N],bo[N],e[N],b;

long long dis[N];

struct edge
{
    int from,v,w,nxt;
}a[N];

void add(int ue,int ve,int we)
{
    a[++tot].nxt=f[ue];
    a[tot].from=ue;
    a[tot].v=ve;
    a[tot].w=we;
    f[ue]=tot;
}

void spfa(int u)
{
    queue<int>que;
    for(int i=1;i<=n;i++)
    {
        e[i]=0;
        bo[i]=0;
        dis[i]=INF; 
    }
    dis[u]=0;
    que.push(u);
    e[u]=1;
    bo[u]=1;
    while(!que.empty())
    {
        int x=que.front();
        que.pop();
        bo[x]=0;
        for(int i=f[x];i!=0;i=a[i].nxt)
        {
//          cout<<x<<" "<<y<<endl;
            int y=a[i].v;
//          cout<<x<<" "<<y<<endl;
            if(dis[y]>dis[x]+a[i].w)
            {
                dis[y]=dis[x]+a[i].w;
                if(bo[y]==0)
                {
                    que.push(y);
                    bo[y]=1;
                    e[y]++;
                    if(e[y]>=n)
                     return;
                }
            }
        } 
    }
}

int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>b>>x>>y;//x,y为结点,b为边权
            add(x,y,b);
            add(y,x,b);
    }
    spfa(1);
    cout<<dis[n]<<endl;
    return 0;
}

注:如果SPFA被卡,可以用duque优化。

3.0各个算法的复杂度

Floyd:O(n3)

Dijkstra:O(n log n)(优化后的)

SPFA:O(mn)

4.0相关题目

 

posted @ 2020-05-10 17:32  Edmundino  阅读(160)  评论(0)    收藏  举报