• 单源最短路径算法小总结


    注意!!!下面的模板有的并没有去设定具体的无法到达的极限值,也没有考虑极限相加爆表的情况,如果可以的话,最好还是把dis数组定义成long long

    Floyd算法(仅仅四行的算法)

    Floyd算法仅仅四行就能解决问题但是时间复杂度达到了感人的O(n^3),唯一的有点是能够输出任意两点之间的最小路径,这或许是他唯一的用途了吧。

    伪代码

    初始化

    赋值如果i==j为0其余为inf

    建图

    Floyd四行代码

    输出

    代码模板

    #include <bits/stdc++.h>
    using namespace std;
    int G[10005][10005];
    int main()
    {
      //n为节点数,m为边的条数
      int n,m;
      cin>>n>>m;
      //初始化,如果i==j那么G[i][j]就是0
      for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
      G[i][j]=i==j?0:999999999;
      //建图
      while(m--)
      {
        int t1,t2,t3;
        cin>>t1>>t2>>t3;
        G[t1][t2]=t3;
      }
      //Floyd算法的核心,4行代码解决
      for(int k=1;k<=n;k++)
      for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
      G[i][j]=min(G[i][j],G[i][k]+G[k][j]);
      //输出答案
      for(int i=1;i<=n;i++)
      {
        for(int j=1;j<=n;j++)
        cout<<G[i][j]<<" ";
        cout<<endl;
      }
      return 0;
    }
    

    Floyd可以解决任意两个节点之间的最短路,而且在稀疏图有着还可以的时间复杂度

    Dijkstra算法

    无优化原始版本

    时间复杂度的进一步的降低,能够算出源节点到各个节点之间的最短路

    伪代码

    初始化

    赋值如果i==j为0其余为inf

    建图

    初始化dis数组与bk数组

    bk[s]=1;

    循环n次

    寻找离要找节点最近的节点

    bk[u]=1;

    尝试每个节点是否能够被这个最近的节点松弛

    循环结束

    输出dis数组

    代码模板

    #include <bits/stdc++.h>
    using namespace std;
    int G[10010][10010];
    int dis[10010];
    int bk[10010];
    int main()
    {
      int n,m,s;
      cin>>n>>m>>s;
      for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
      G[i][j]=i==j?0:999999999;
      while(m--)
      {
        int t1,t2,t3;
        cin>>t1>>t2>>t3;
        G[t1][t2]=t3;
      }
      for(int i=1;i<=n;i++)
      dis[i]=G[s][i];
      memset(bk,0,sizeof(bk));
      bk[s]=1;
      for(int i=1;i<=n;i++)
      {
        int x,minn=999999999;
        for(int j=1;j<=n;j++)
        if(dis[j]<minn&&!bk[j])
        minn=dis[x=j];
        bk[x]=1;
        for(int j=1;j<=n;j++)
        dis[j]=min(dis[j],dis[x]+G[x][j]);
      }
      for(int i=1;i<=n;i++)
      cout<<dis[i]<<" ";
      return 0;
    }
    

    复杂度O(n^2)

    vector建图优化

    因为我们用邻接矩阵建图很有可能会爆内存,所以可以采用vector数组建图优化。遇到较大的数据可以采用。

    vector优化模板

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    struct node
    {
      int to,cost;
    };
    vector<node> G[500005];
    int bk[500005];
    ll dis[500005]; 
    const int inf=INT_MAX;
    int main()
    {
    	//freopen("data.in","r",stdin);
      int m,n,s;
      cin>>n>>m>>s;
      while(m--)
      {
        int t1,t2,t3;
        cin>>t1>>t2>>t3;
    	node e;
        e={t2,t3};
        G[t1].push_back(e);
      }
      for(int i=1;i<=n;i++)
      dis[i]=i==s?0:inf;
      for(int i=1;i<=n;i++)
      {
        int x,y=inf;
        for(int j=1;j<=n;j++)
        if(!bk[j]&&dis[j]<=y)
        y=dis[x=j];
        bk[x]=1;
        for(int j=0;j<G[x].size();j++)
        dis[G[x][j].to]=min(dis[G[x][j].to],dis[x]+G[x][j].cost);
      }
      for(int i=1;i<=n;i++)
      cout<<dis[i]<<" ";
    }
    

    实际上根据题目的要求如果无法到达的话要进行各种的输出,最好定义dis数组为ll为妙

    最终优化vector+优先队列

    直接硬性把时间设置成MlogN

    模板代码

    #include <bits/stdc++.h>
    using namespace std;
    struct node //存入优先队列中的结构体
    {
      int num;
      int cost;
      bool operator < (const node&a) const
      {
        return cost>a.cost;
      }
    };
    struct edge//存入vector中的
    {
      int to,cost;
    };
    int dis[10005];
    int bk[10005];
    const int inf=INT_MAX;
    vector <edge> G[10005];
    priority_queue <node> q;
    int main()
    {
      int n,m,s;
      cin>>n>>m>>s;
      while(m--) //建图
      {
        int t1,t2,t3;
        cin>>t1>>t2>>t3;
        edge e={t2,t3};
        G[t1].push_back(e);
      }
      for(int i=1;i<=n;i++)//初始化dis数组
      dis[i]=i==s?0:inf;
      bk[s]=1;
      q.push((node){s,0});
      while(!q.empty())
      {
        node a=q.top();
        q.pop();
        if(dis[a.num]!=a.cost)//判断条件剪枝
        continue;
        bk[a.num]=1;
        for(int i=0;i<G[a.num].size();i++)
        if(!bk[G[a.num][i].to])
        {
          if(dis[G[a.num][i].to]>dis[a.num]+G[a.num][i].cost)
          {
            dis[G[a.num][i].to]=dis[a.num]+G[a.num][i].cost;
            q.push((node){G[a.num][i].to,dis[G[a.num][i].to]});
          }
        }
      }
      for(int i=1;i<=n;i++)
      cout<<dis[i]<<" ";
    }
    

    Bellman-Ford算法(真的三行。。。。)

    基本的思路与dijkstra差不多,但是能够解决负权图

    伪代码

    初始化

    初始化dis数组

    Bellman-Ford算法

    输出dis数组

    无优化版本

    #include <bits/stdc++.h>
    using namespace std;
    int num[500005],to[500005],cost[500005];
    long long dis[100005];
    const int inf=INT_MAX;
    int main()
    {
      int n,m,s;
      cin>>n>>m>>s;
      for(int i=1;i<=m;i++)
      cin>>num[i]>>to[i]>>cost[i];
      for(int i=1;i<=n;i++)
      dis[i]=i==s?0:inf;
      for(int k=1;k<=n-1;k++)
      for(int i=1;i<=m;i++)
      dis[to[i]]=min(dis[to[i]],dis[num[i]]+cost[i]);
      for(int i=1;i<=n;i++)
      cout<<dis[i]<<" ";
    }
    

    SPFA

    近似于无敌的算法,但是对于解决非负权边的单源最短路还是用dij+优先队列优化得了

    沙雕图

    废话少说直接上模板

    #include <bits/stdc++.h>
    using namespace std;
    struct edge
    {
      int to,cost;
    };
    int dis[100005];
    int bk[100005];
    vector<edge> G[100005];
    queue<int> q;
    const int inf=INT_MAX;
    int main()
    {
      int n,m,s;
      cin>>n>>m>>s;
      while(m--)
      {
        int t1,t2,t3;
        cin>>t1>>t2>>t3;
        G[t1].push_back((edge){t2,t3});
      }
      for(int i=1;i<=n;i++)
      dis[i]=i==s?0:inf;
      q.push(s);
      while(!q.empty())
      {
        int p=q.front();
        q.pop();
        bk[p]=0;
        for(int i=0;i<G[p].size();i++)
        {
          if(dis[G[p][i].to]>dis[p]+G[p][i].cost)
          {
            dis[G[p][i].to]=dis[p]+G[p][i].cost;
            if(!bk[G[p][i].to])
            {
              q.push(G[p][i].to);
              bk[G[p][i].to]=1;
            }
          }
        }
      }
      for(int i=1;i<=n;i++)
      cout<<dis[i]<<" ";
    }
    

    总结

    如果每个节点之间的最小路径需要求出的话,那么还是用Floyd吧,对于非负图看n和m如果m都比n大了,那么最好还是老老实实的用Dijkstra+优先队列吧,如果出现负权图,那么还是老老实实SPFA

  • 相关阅读:
    解决svn中文乱码的问题
    C#使用SQLite出错:无法加载 DLL“SQLite.Interop.dll”,找不到指定的模块
    手把手教你使用C#操作SQLite数据库,新建数据库,创建表,插入,查询,删除,运算符,like
    c# 串口SerialPort
    spy++使用指南
    FindWindow用法
    其他信息: 线程间操作无效: 从不是创建控件“控件名”的线程访问它。
    SVN设置必须锁定
    利用webBrowser获取页面iframe中的内容
    谨慎注意WebBrowser控件的DocumentCompleted事件
  • 原文地址:https://www.cnblogs.com/baccano-acmer/p/9889026.html
Copyright © 2020-2023  润新知