码迷,mamicode.com
首页 > 编程语言 > 详细

最短路:spfa算法

时间:2017-09-08 21:44:14      阅读:209      评论:0      收藏:0      [点我收藏+]

标签:++   mem   模板题   奶牛   存在   入队   存储   ble   优化   

板子补完计划绝赞继续中(

这篇博客就来写一写spfa(这我居然板子都打错了一次,我太弱啦!)

先来看一下定义:(引自http://blog.csdn.net/juststeps/article/details/8772755)

首先说明,SPFA是一种单源最短路径算法,所以以下所说的“某点的最短路径长度”,指的是“某点到源点的最短路径长度”。

我们记源点为S,由源点到达点i的“当前最短路径”为D[i],开始时将所有D[i]初始化为无穷大,D[S]则初始化为0。算法所要做的,就是在运行过程中,不断尝试减小D[]数组的元素,最终将其中每一个元素减小到实际的最短路径。

过程中,我们要维护一个队列,开始时将源点置于队首,然后反复进行这样的操作,直到队列为空:

(1)从队首取出一个结点u,扫描所有由u结点可以一步到达的结点,具体的扫描过程,随存储方式的不同而不同;

(2)一旦发现有这样一个结点,记为v,满足D[v] > D[u] + w(u, v),则将D[v]的值减小,减小到和D[u] + w(u, v)相等。其中,w(u, v)为图中的边u-v的长度,由于u-v必相邻,所以这个长度一定已知(不然我们得到的也不叫一个完整的图);这种操作叫做松弛。

引用内容
松弛操作的原理是著名的定理:“三角形两边之和大于第三边”,在信息学中我们叫它三角不等式。所谓对i,j进行松弛,就是判定是否d[j]>d[i]+w[i,j],如果该式成立则将d[j]减小到d[i]+w[i,j],否则不动。

(3)上一步中,我们认为我们“改进了”结点v的最短路径,结点v的当前路径长度D[v]相比于以前减小了一些,于是,与v相连的一些结点的路径长度可能会相应地减小。注意,是可能,而不是一定。但即使如此,我们仍然要将v加入到队列中等待处理,以保证这些结点的路径值在算法结束时被降至最优。当然,如果连接至v的边较多,算法运行中,结点v的路径长度可能会多次被改进,如果我们因此而将v加入队列多次,后续的工作无疑是冗余的。这样,就需要我们维护一个bool数组Inqueue[],来记录每一个结点是否已经在队列中。我们仅将尚未加入队列的点加入队列。


算法能否结束?

对于不存在负权回路的图来说,上述算法是一定会结束的。因为算法在反复优化各个最短路径长度,总有一个时刻会进入“无法再优化”的局面,此时一旦队列读空,算法就结束了。然而,如果图中存在一条权值为负的回路,就糟糕了,算法会在其上反复运行,通过“绕圈”来无休止地试图减小某些相关点的最短路径值。假如我们不能保证图中没有负权回路,一种“结束条件”是必要的。这种结束条件是什么呢?思考Bellman-Ford算法,它是如何结束的?显然,最朴素的Bellman-Ford算法不管循环过程中发生了什么,一概要循环|V|-1遍才肯结束。凭直觉我们可以感到,SPFA算法“更聪明一些”,就是说我们可以猜测,假如在SPFA中,一个点进入队列——或者说一个点被处理——超过了|V|次,那么就可以断定图中存在负权回路了。

     定义从来就不是我们要学习的重点 我们要优雅の打板子!

   来看这一道比奶牛热浪还裸的模板题:

最短路

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 22670    Accepted Submission(s): 9663


Problem Description
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?


Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。


Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间


Sample Input
2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0


Sample Output
3 2


Source


Recommend
      然而似乎还是不够裸..
   我们取消掉路是双向的这样一个条件 但结构允许出现负环,并且只有一组数据,
   就可以用下面的代码实现啦
#pragma GCC optimize("O2")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<limits.h>
#include<ctime>
#define N 100001
typedef long long ll;
const int inf=0x3fffffff;
const int maxn=2017;
using namespace std;
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch>‘9‘||ch<‘0‘)
    {
        if(ch==‘-‘)
        f=-1;
        ch=getchar();
    }
    while(ch<=‘9‘&&ch>=‘0‘)
    {
        x=(x<<3)+(x<<1)+ch-‘0‘;
        ch=getchar();
    }
    return f*x;
}
struct tsdl{
	int to,w,next;
}edge[N*4];
int tot,head[N],inq[N],d[N],n,m,cnt[N];
queue<int>q;

void add(int ui,int vi,int wi)
{
	edge[++tot].next=head[ui];
	edge[tot].w=wi;
	edge[tot].to=vi;
	head[ui]=tot;
} 

bool spfa(int u)
{
	q.push(u);
	for(int i=1;i<=n;i++)
	d[i]=inf;
	d[u]=0,inq[u]=1;
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		inq[x]=0;
		for(int i=head[x];i!=-1;i=edge[i].next)
		{
			int v=edge[i].to;
			if(d[v]>d[x]+edge[i].w)
			d[v]=d[x]+edge[i].w;
			if(!inq[v])
			{
				q.push(v);
				inq[v]=1;
				if(++cnt[v]>n)return 1;
			}
		}
	}
	return 0;
}

int main()
{
	memset(head,-1,sizeof(head));
	n=read(),m=read();
	int ts=read(),te=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),w=read();
		add(u,v,w);
	}
	if(spfa(ts)||d[te]==inf)
	{
		cout<<-1;
		return 0;
	}
	cout<<d[te];
}

  一期非常蒟蒻的模板总结 以上desu

最短路:spfa算法

标签:++   mem   模板题   奶牛   存在   入队   存储   ble   优化   

原文地址:http://www.cnblogs.com/tsunderehome/p/7496278.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!