單源最短路徑算法小總結


注意!!!下面的模板有的並沒有去設定具體的無法到達的極限值,也沒有考慮極限相加爆表的情況,如果可以的話,最好還是把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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM