`
yzmduncan
  • 浏览: 326601 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

单源最短路之SPFA——Shortest Path Faster Algorithm

阅读更多

   SPFA是目前相当优秀的求最短路径的算法,值得我们掌握。
   SPFA对Bellman-Ford算法优化的关键之处在于意识到:只有那些在前一遍松弛中改变了距离估计值的点,才可能引

起他们的邻接点的距离估计值的改变。因此,用一个先进先出的队列来存放被成功松弛的顶点。初始时,源点s入队。

当队列不为空时,取出对首顶点,对它的邻接点进行松弛。如果某个邻接点松弛成功,且该邻接点不在队列中,则将其

入队。经过有限次的松弛操作后,队列将为空,算法结束。SPFA算法的实现,需要用到一个先进先出的队列 queue 和

一个指示顶点是否在队列中的 标记数组 mark。为了方便查找某个顶点的邻接点,图采用临界表存储。
   需要注意的是:仅当图不存在负权回路时,SPFA能正常工作。如果图存在负权回路,由于负权回路上的顶点无法收

敛,总有顶点在入队和出队往返,队列无法为空,这种情况下SPFA无法正常结束。
判断负权回路的方案很多,世间流传最广的是记录每个结点进队次数,超过|V|次表示有负权
关于SPFA的时间复杂度,一般认为是 O(kE),k一般为2。
如果在判断是否存在负权回路,推荐使用第一种优化形式,否则推荐使用SPFA。

 

//题目要求:判断有无负权环
#include <iostream>
using namespace std;
const int N = 502;
const int MAX = INT_MAX;
int f;
int n;					//节点个数为n,边数为2*m+w
int m;
int w;
int queue[N],time[N],dis[N];	//循环队列,记录每个节点入队列的次数(大于n表示有负权环),dis记录最短路径值
bool vis[N];			//true表示在队列中。被松弛的点若在队列中,就不加入队列,出队列后值为false
int pre[N];			//节点i发出若干条边,pre[i]表示节点i当前正在遍历的边的前一条边的序号
typedef struct
{
	int to;
	int w;
	int next;
}Edge;
Edge e[5201];

bool spfa()
{
	int i,j,now,adj;
	memset(vis,false,sizeof(vis));
	memset(time,0,sizeof(time));
	for(i = 2, dis[1] = 0; i <= n; i++)
		dis[i] = MAX;
	queue[0] = 1;
	vis[1] = true;
	time[1] = 1;
	int head = 0,tail = 1;
	while(head != tail)
	{
		now = queue[head];
		head = (head + 1) % n;
		vis[now] = false;
		for(i = pre[now]; i != -1; i = e[i].next)
		{
			adj = e[i].to;
			if(dis[now] != MAX && dis[now] + e[i].w < dis[adj])
			{
				if(adj == 1)		//若初始点1本身包含负环,直接返回
					return true;
				dis[adj] = dis[now] + e[i].w;
				if(!vis[adj])
				{
					queue[tail] = adj;
					tail = (tail + 1) % n;
					vis[adj] = true;
					time[adj] ++;
					if(time[adj] > n)
						return true;
				}
			}
		}
	}
	return false;
}

int main()
{
	int i,j,edgeNum;
	int from,to,w1;
	cin>>f;
	for(i = 0; i < f; i++)
	{
		edgeNum = 0;
		memset(pre,-1,sizeof(pre));
		memset(e,0,sizeof(e));
		cin>>n>>m>>w;
		for(j = 1; j <= m; j++)
		{
			cin>>from>>to>>w1;
			e[edgeNum].to = to; e[edgeNum].w = w1; e[edgeNum].next = pre[from];
			pre[from] = edgeNum;
			edgeNum++;
			e[edgeNum].to = from; e[edgeNum].w = w1; e[edgeNum].next = pre[to];
			pre[to] = edgeNum;
			edgeNum++;
		}
		for(j = 1; j <= w; j++)
		{
			cin>>from>>to>>w1;
			e[edgeNum].to = to; e[edgeNum].w = -w1; e[edgeNum].next = pre[from];
			pre[from] = edgeNum;
			edgeNum++;
		}
		if(spfa())
			cout<<"YES\n";
		else
			cout<<"NO\n";
	}
	return 0;
}

    

    参见poj3259,1511

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics