单源最短路径算法小总结

注意!!!下面的模板有的并没有去设定具体的无法到达的极限值,也没有考虑极限相加爆表的情况,如果可以的话,最好还是把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

原文地址:https://www.cnblogs.com/baccano-acmer/p/9889026.html