Bellman算法和SPFA算法

Dijkstra算法比较快速,但是如果遇到负边就无能为力了,而Bellman算法可以解决负边问题,只要不是负环。
这个算法数据结构没有讲过,他主要是每次对所以边进行松弛操作,进行n-1次得到最短路径。
其原理如下所述:
首先指出,图的任意一条最短路径既不能包含负权回路,也不会包含正权回路,因此它最多包含|n-1条边。
其次,从源点s可达的所有顶点如果存在最短路径,则这些最短路径构成一个以s为根的最短路径树。Bellman-Ford算法的迭代松弛操作,实际上就是按顶点距离s的层次,逐层生成这棵最短路径树的过程。
在对每条边进行1遍松弛的时候,生成了从s出发,层次至多为1的那些树枝。也就是说,找到了与s至多有1条边相联的那些顶点的最短路径;对每条边进行第2遍松弛的时候,生成了第2层次的树枝,就是说找到了经过2条边相连的那些顶点的最短路径……。因为最短路径最多只包含|v|-1 条边,所以,只需要循环|v|-1 次。
如果没有负权回路,由于最短路径树的高度最多只能是|v|-1,所以最多经过|v|-1遍松弛操作后,所有从s可达的顶点必将求出最短距离。如果 d[v]仍保持 +∞,则表明从s到v不可达。
如果有负权回路,那么第 |v|-1 遍松弛操作仍然会成功,这时,负权回路上的顶点不会收敛。

根据这个原理写出代码如下:

bool Bellman(int s,int n){
	
	fill(dis,dis+n,inf);
	dis[s]=0;
	
	for(int i=0;i<n-1;i++){//n-1此松弛操作 
	    int flag=1;
		for(int u=0;u<n;u++) 
		{
			for(int j=0;j<Adj[u].size();j++)
			{
				int x=Adj[u][j].v;
			if(dis[u]+Adj[u][j].weight<dis[x])
			 {dis[x]=dis[u]+Adj[u][j].weight;//松弛操作  
			 flag=0;
			}	
			}
		}
		if(flag)return true;//如果这一轮没有被松弛,直接退出 
      }
	for(int u=0;u<n;u++) //判断是否存在负环 
		{
			for(int j=0;j<Adj[u].size();j++)
			{
				int x=Adj[u][j].v;
			if(dis[u]+Adj[u][j].weight<dis[x])
			  return false;//还能松弛存在负环,失败 
			}	
		}
		return true;	
	}

虽然理解简单但是这个算法耗时较大,每一轮都要遍历所有边,其实由于一个顶点u的d[u]改变时,以此点出发的邻接点d[v]才可能改变,结合Bellman树的形成过程可以类比层次遍历,用一个队列存放结点,每拿出一个结点对邻接点松弛,如果能松弛且不在队列里就让其入队,直到队空(说明无负环,)或者某个结点入队超过n-1次,说明有负环。这就是SPFA算法,一般比Dijkstra算法还要快,比较高效。代码如下:

bool SPFA(int s,int n){
		fill(inq,inq+n,false);//记录是否入队
		fill(num,num+n,0);//记录入队次数
		fill(dis,dis+n,inf);
		queue<int>q;
		q.push(s);
		inq[s]=true;
		num[s]++;
		dis[s]=0;
		while(!q.empty()){
			int u=q.front();q.pop();
			inq[u]=false;
			for(int j=0;j<Adj[u].size();j++){
				int x=Adj[u][j].v;
			if(dis[u]+Adj[u][j].weight<dis[x]){
			  dis[x]=dis[u]+Adj[u][j].weight;
				if(!inq[x])
				{
					q.push(x);
					num[x]++;
					inq[x]=true;
					if(num[x]>n-1)//负环
					return false;
				}
			}
			}		
		}
	return true;	
	}

此外关于最短路径还有一个Floyd算法,这里打包带走,下面是三个算法的全体代码:

#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;

const int maxv=101;
const int inf=10000000;
struct node{
	int v,weight;
};

vector<node>Adj[maxv] ;
int dis[101];

//bellman算法 

bool Bellman(int s,int n){
	
	fill(dis,dis+n,inf);
	dis[s]=0;
	
	for(int i=0;i<n-1;i++){//n-1此松弛操作 
	    int flag=1;
		for(int u=0;u<n;u++) 
		{
			for(int j=0;j<Adj[u].size();j++)
			{
				int x=Adj[u][j].v;
			if(dis[u]+Adj[u][j].weight<dis[x])
			 {dis[x]=dis[u]+Adj[u][j].weight;//松弛操作  
			 flag=0;
			}	
			}
		}
		if(flag)return true;//如果这一轮没有被松弛,直接退出 
      }
	for(int u=0;u<n;u++) //判断是否存在负环 
		{
			for(int j=0;j<Adj[u].size();j++)
			{
				int x=Adj[u][j].v;
			if(dis[u]+Adj[u][j].weight<dis[x])
			  return false;//还能松弛存在负环,失败 
			}	
		}
		return true;	
	}
	
	
	
	//SPFA算法
	bool inq[maxv] ;
	int num[maxv];
	
	bool SPFA(int s,int n){
		fill(inq,inq+n,false);
		fill(num,num+n,0);
		fill(dis,dis+n,inf);
		queue<int>q;
		q.push(s);
		inq[s]=true;
		num[s]++;
		dis[s]=0;
		while(!q.empty()){
			int u=q.front();q.pop();
			inq[u]=false;
			for(int j=0;j<Adj[u].size();j++){
				int x=Adj[u][j].v;
			if(dis[u]+Adj[u][j].weight<dis[x]){
			  dis[x]=dis[u]+Adj[u][j].weight;
				if(!inq[x])
				{
					q.push(x);
					num[x]++;
					inq[x]=true;
					if(num[x]>n-1)
					return false;
				}
			}
			}		
		}
	return true;	
	}


//floyd算法
int d[maxv][maxv]	;

void floyd(int n){
	for(int k=0;k<n;k++)
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++)
	if(d[i][k]!=inf&&d[k][j]!=inf&&d[i][k]+d[k][j]<d[i][j])
	d[i][j]=d[i][k]+d[k][j];
}
	
	
	int main(){
		int n,m,s,t;
	int x,y,w;
	node temp;
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=0;i<m;i++){
		scanf("%d%d%d",&x,&y,&w); 
		temp.weight=w;
		temp.v=x;
		Adj[y].push_back(temp);
		temp.v=y;
		Adj[x].push_back(temp);
	}
	if(SPFA(s,n)) {
		for(int i=0;i<n;i++)
		printf("%d ",dis[i]);
	}
		return 0;
	} 
发布了63 篇原创文章 · 获赞 6 · 访问量 2014

猜你喜欢

转载自blog.csdn.net/qq_36684096/article/details/104729213
今日推荐