单源最短路径的SPFA算法

    SPFA算法用于求单源最短路径,是Bellman-Ford算法的队列优化。尽管有人指出该算法O(E)的复杂度的理论证明是错误的,且在特别针对的图上,SPFA算法非常慢。而Dijkstra算法的理论证明严谨,表现也十分稳定。但是Dijkstra算法不能用于负权值的图,而Bellman-Ford算法效率又十分的低。所以仍然有很多人倾向于使用SPFA算法(SPFA实现比较简单,有人认为比Dijkstra和BF更简单)。实践表明,SPFA算法是完全可行的。一来理论证明错误,并不代表效率不能接受;二来也没有见过专卡SPFA的题目数据。近些年越来越多的题目开始卡SPFA,不过能解决负权值与负环,仍然是一个优点。所以,优先使用Dijkastra,然后再考虑SPFA。

SPFA的算法流程简单描述如下:
D数组保存源s到各点的距离,初始D[s] = 0,而其他个点设置为极大值例如INT_MAX
F数组用于保存标志位,表示对应点是否在队列中,初始全为false
C数组用于保存各点的入队次数,初始全为0。若某点的入队次数达到n次(n为顶点总数),则存在负环
建立队列,将s入队
当队列不为空时{
    取出头结点u
    对u的每一条出边对应的点v{
        做松弛操作
        如果v不在队列,则入队并维护F和C
        若入队次数达到n次,有负环,结束算法
    }
}

    松弛操作就是维护D数组,算法结束后D数组就是结果,如果存在D[u]==INT_MAX,说明s和u是不连通的。
    如果确定无负环,则可不必维护C数组。

    

    下面是hdu1874的源代码,典型的单源最短路径,无负权值。

 

#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;

typedef int weight_t; 

#define SIZE 2005

int N,M;
int S,T;

//边的结构
struct edge_t{
	int node;
	weight_t w;
	edge_t* next;
}Edge[SIZE];
int ECnt = 0;

//图的邻接表
edge_t* Ver[202];

//初始化
inline void init(){
	ECnt = 0;
	fill(Ver,Ver+N,(edge_t*)0);
}

//生成双向边
inline void mkEdge(int a,int b,weight_t w){
	Edge[ECnt].node = b;
	Edge[ECnt].w = w;
	Edge[ECnt].next = Ver[a];
	Ver[a] = Edge + ECnt ++;

	Edge[ECnt].node = a;
	Edge[ECnt].w = w;
	Edge[ECnt].next = Ver[b];
	Ver[b] = Edge + ECnt ++;
}

//最短路径的结果
weight_t D[202];

//标志位
bool Flag[202];

//spfa算法,输入保证没有负环,s为源
void spfa(int s){
	//初始化
	fill(D,D+N,INT_MAX);
	fill(Flag,Flag+N,false);

	//初始化源
	D[s] = 0;
	Flag[s] = true;

	//队列
	queue<int> q;
	q.push(s);

	while( !q.empty() ){
		int u = q.front();
		q.pop();
		Flag[u] = false;

		//对u的每一条边
		for(edge_t*p=Ver[u];p;p=p->next){
			int v = p->node;
			weight_t tmp = D[u] + p->w;
			if ( D[v] > tmp ){
				D[v] = tmp;
				if ( !Flag[v] )
					Flag[v] = true, q.push(v);
			}
		}
	}

	return;
}

bool read(){
	if ( EOF == scanf("%d%d",&N,&M) )
		return false;

	init();
	for(int i=0;i<M;++i){
		int a,b,w;
		scanf("%d%d%d",&a,&b,&w);
		mkEdge(a,b,w);
	}
	scanf("%d%d",&S,&T);

	return true;
}

int main(){
	while( read() ){
		spfa(S);
		if ( INT_MAX == D[T] ) printf("-1\n");
		else printf("%d\n",D[T]);
	}
	return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值