图论最短路径算法(二):弗洛伊德(Floyd)算法 与 SPFA

弗洛伊德算法

Floyd算法可以计算出一个带权图中任意两点间的最短路径(可以有负权),用到了动态规划DP的思想(这部分还没有看过,只简单看了一下操作,以后专门找时间来学习)。I,j的最短路径有两种可能:
1.i直接到j
2.i通过另一个顶点k到达j
我们只需要检查这两种情况哪一个距离更小,再更新I到j的距离就行了
写起来非常简单,它的核心就是一个状态转移方程:
第K个状态:
DP(k)[i,j]=min ( DP(k-1)[i,k] + DP(k-1)[k,j] , DP(k-1)[i,j] )
即判断:

judge ( DP[i,j]>DP[i,k] + DP[k,j]) ?
if true : DP[i,j] = DP[i,k]+DP[k,j]  

大概是这个样子,这个算法需要I,J,K三重循环,故算法时间复杂度为O(n^3),虽然编码容易,但提交应该经常会爆掉…顺手写个模板:

#include<iostream>
#include<string.h>
const int MAXN = 101;
const int INF = 0x3f3f3f3f;
//Description:this is a program for Algorithm-Floyd
int Matrix[MAXN][MAXN],DP[MAXN][MAXN];
int m,n;
//Define
int min(int A,int B)
{
 if(A<B)return A;
 else return B; 
}//Get the min value
void CreateGraph()
{
 memset(Matrix,INF,sizeof(Matrix));
 memset(DP,INF,sizeof(DP));
 int Estart,Eend,Weight;
 std::cin>>n>>m;//input vertexnum and edgenum
 for(int i=1;i<=m;i++)
 {
  std::cin>>Estart>>Eend>>Weight;
  Matrix[Estart][Eend]=Weight;
  Matrix[Eend][Estart]=Weight;
  DP[Estart][Eend]=Weight;
  DP[Eend][Estart]=Weight;
  //assume that it is a normal graph
 }
 for(int i=1;i<=n;i++)
 {
  Matrix[i][i]=0;
 }
}//Get the Graph 
//
void Floyd()
{
 for(int k=1;k<=n;k++)
 {
  for(int i=1;i<=n;i++)
  {
   for(int j=1;j<=n;j++)
   {
    DP[i][j]=min(DP[i][k]+DP[k][j],DP[i][j]);
   }
  }
 }
}
//
int main()
{
 CreateGraph();
 Floyd();
 for(int i=1;i<=n;i++)
 {
  for(int j=1;j<=n;j++)
  {
   std::cout<<DP[i][j]<<" ";
  }
  std::cout<<"\n";
 }
}

SPFA

SPFA算法的全称为Shortest Path Faster Algorithm,是用队列优化后的Bellman—Ford算法。
Dijkstra算法固然有用,但一遇到了负权边就炸了,这时候就需要这种算法来处理含负数权边的最短路径问题。它用到的更新最短路的方法是松弛操作,与Floyd算法是一样的:

judge ( DP[i,j]>DP[i,k] + DP[k,j]) ?
if true : DP[i,j] = DP[i,k]+DP[k,j]  

但我们用的是队列来进行结点的存取,有点BFS搜索的意思,不过BFS中结点出了队就不会再进去了,SPFA则不一定。关于打印路径的问题,我选择定义一个前驱数组,存放每一个点的前一个点,规定起点的值为自己,在满足最短路更新条件时就同时更新前驱数组。再写一个递归打印的函数,满足x等于pre[x]就打印,否则继续递归。也想过用vector来存路径,好像不太好使,还费空间,被我注释掉了,想看就看看吧。直接上代码,注释写得比较清楚了:

#include<iostream>
#include<string.h>
#include<queue>
#include<vector>
using std::queue;
using std::vector;
const int MAXN = 101;
const int INF = 0x3f3f3f3f;
//Description:this is a program for Algorithm-SPFA
int Matrix[MAXN][MAXN],Dis[MAXN],Vis[MAXN];
int pre[MAXN];
int m,n;
queue<int> Q;
vector<int> Path[MAXN];
//Define
int min(int A,int B)
{
 if(A<B)return A;
 else return B; 
}//Get the min value
void Print(int x)
{
 if(x!=pre[x])
 {
  Print(pre[x]);
 }
 std::cout<<x<<" "; 
}
void CreateGraph()
{
 memset(Matrix,INF,sizeof(Matrix));
 int Estart,Eend,Weight;
 std::cin>>n>>m;//input vertexnum and edgenum
 for(int i=1;i<=m;i++)
 {
  std::cin>>Estart>>Eend>>Weight;
  Matrix[Estart][Eend]=Weight;
  Matrix[Eend][Estart]=Weight;
  //assume that it is a normal graph
 }
 for(int i=1;i<=n;i++)
 {
  Matrix[i][i]=0;
 }
}//Get the Graph
//
void SPFA(int st)
{
 memset(Dis,INF,sizeof(Dis));
 memset(Vis,0,sizeof(Vis));
 Dis[st]=0;//start point
 Q.push(st);//enqueue st point
 Vis[st]=1;
 pre[st]=st;
 while(!Q.empty())
 {
  int v=Q.front();
  Q.pop();//dequeue
  Vis[v]=1;//update
  for(int u=1;u<=n;u++)
  {
   if(Dis[u]>Dis[v]+Matrix[v][u])
   {
    Dis[u]=Dis[v]+Matrix[v][u];//relaxation
    Path[u].push_back(v);
    pre[u]=v;
    if(Vis[u]==0)//unreached
    {
     Q.push(u);//enqueue
     Vis[u]=1;
    }
   }
  }
 }
}
int main()
{
 CreateGraph();
 SPFA(1);
 vector<int>::iterator iter1;
 for(int i=1;i<=n;i++)
 {
  std::cout<<Dis[i]<<" ";
 }
/* for(int i=1;i<=n;i++)
 {
  iter1=Path[i].begin();
  while(iter1!=Path[i].end())
  {
   std::cout<<*iter1<<"-"; 
   iter1++;
  }
  std::cout<<i;
  std::cout<<"end"<<std::endl;
 }*/
 for(int i=1;i<=n;i++)
 {
  Print(i);
  std::cout<<"\n";
 }
 std::cout<<"\n";
 return 0;
}

试一下之前的样例:
5 6
1 2 5
1 3 8
2 3 1
2 4 3
4 5 7
2 5 2

7 11
1 3 2
1 2 15
1 4 10
2 5 6
3 5 7
3 6 4
4 7 4
5 7 9
6 7 10
6 4 5
7 2 4

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值