Bellman-Ford算法
Dijstra算法可以很好地解决无负权图的最短路径问题,但如果出现了负权边,Dijstra算法会失效。
Bellman-Ford和Dijstra一样,也可以解决单源最短路径问题,同时能处理有负权边的情况。
由于Bellman-Ford算法需要遍历所有边,所以使用邻接表会比邻接矩阵方便
struct Node{
int v,dis;//v为邻接边的目标顶点,dis为邻接边的边权
};
vector<Node> Adj[maxv]; //图G的邻接表
int n;//n为顶点数,maxv为最大顶点数
int d[maxv]; //起点到达各边的最短路径长度
bool Bellman(int s){
fill(d,d+maxv,inf);
d[s]=0;
for(int i=0;i<n-1;i++){
for(int u=0;u<n;u++){
for(int j=0;j<Adj[u].size();j++){
int v=Adj[u][j].v;
int dis=Adj[u][j].dis;
if(d[u]+dis<d[v]){
d[v]=d[u]+dis;
}
}
}
}
// 以下为判断负环的代码
for(int u=0;u<n;u++){
for(int j=0;j<Adj[u].size();j++){
int v=Adj[u][j].v;
int dis=Adj[u][j].dis;
if(d[u]+dis<d[v]){
return false;
}
}
}
return true;
}
由于Bellman-Ford算法的时间复杂度达到了O(ve),这并不如意,所以需要进行优化,
Bellman-Ford的每轮操作都需要操作所有边,这其中会有大量无意义操作,严重影响了算法性能。
由于,只有当某个顶点u的d[u]值改变时,从它出发的邻接点v的d[v]值才有可能被改变。
所以可以如下优化:
建立一个队列,每次将队首顶点u取出,然后对从u出发的所有边u->v进行松弛操作,
即判断d[u]+length[u->v]
SPFA算法
上述被优化后的算法即为SPFA算法,期望时间复杂度为O(ve)
但如果图中有从源点可达的负环,则SPFA的时间复杂度又会退化为O(ve)
vector<Node> ADj[maxv];
int n,d[maxv],num[maxv];
bool inq[maxv];
bool SPFA(int s){
memset(inq,false,sizeof(inq));
memset(num,0,sizeof(num));
fill(d,d+maxv,inf);
queue<int> q;
q.push(s);
inq[s]=true;
num[s]++;
d[s]=0;
while(q.empty()){
int u=q.front();
q.pop();
inq[u]=false;
for(int j=0;j<Adj[u].size();j++){
int v=Adj[u][j].v;
int dis=Adj[u][j].dis;
if(d[u]+dis<d[v]){
d[v]=d[u]+dis;
if(!inq[v]){
q.push(v);
inq[v]=true;
num[v]++;
if(num[v]>=n) return false;
}
}
}
}
return true;
}
Flord算法
Flord算法用来解决全源最短路问题。
即对給定的图G(v,e),求任意两点u,v之间的最短路径长度,时间复杂度为O(n^3)所以顶点数要限制约在200以内。
用邻接矩阵来实现Flord算法比较合适。
#include<cstdio>
#include<algorithm>
using namespace std;
const int inf =0x3f3f3f3f;
const int maxv =200;
int n,m;
int dis[maxv][maxv];
void Flord(){
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(dis[i][k]!=inf&&dis[k][j]!=inf&&dis[i][k]+dis[k][j]<dis[i][j]){
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
}
}
int main()
{
int u,v,w;
fill(dis[0],dis[0]+maxv*maxv,inf);
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
dis[i][i]=0;
}
for(int i=0;i<m;i++){
scanf("%d%d%d",&u,&v,&w);
dis[u][v]=w;
}
Flord();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%d ",dis[i][j]);
}
printf("\n");
}
return 0;
}