hdu-2066 一个人的旅行(SPFA做法)

一个人的旅行

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 24912    Accepted Submission(s): 8659


Problem Description
虽然草儿是个路痴(就是在杭电待了一年多,居然还会在校园里迷路的人,汗~),但是草儿仍然很喜欢旅行,因为在旅途中 会遇见很多人(白马王子,^0^),很多事,还能丰富自己的阅历,还可以看美丽的风景……草儿想去很多地方,她想要去东京铁塔看夜景,去威尼斯看电影,去阳明山上看海芋,去纽约纯粹看雪景,去巴黎喝咖啡写信,去北京探望孟姜女……眼看寒假就快到了,这么一大段时间,可不能浪费啊,一定要给自己好好的放个假,可是也不能荒废了训练啊,所以草儿决定在要在最短的时间去一个自己想去的地方!因为草儿的家在一个小镇上,没有火车经过,所以她只能去邻近的城市坐火车(好可怜啊~)。
 

Input
输入数据有多组,每组的第一行是三个整数T,S和D,表示有T条路,和草儿家相邻的城市的有S个,草儿想去的地方有D个;
接着有T行,每行有三个整数a,b,time,表示a,b城市之间的车程是time小时;(1=<(a,b)<=1000;a,b 之间可能有多条路)
接着的第T+1行有S个数,表示和草儿家相连的城市;
接着的第T+2行有D个数,表示草儿想去地方。
 

Output
输出草儿能去某个喜欢的城市的最短时间。
 

Sample Input
  
  
6 2 3 1 3 5 1 4 7 2 8 12 3 8 4 4 9 12 9 10 2 1 2 8 9 10
 

Sample Output
  
  
9
 

Author
Grass
# include<queue>
# include<stack>
# include<cstdio>
# include<cstring>
# include<algorithm>
# define MAXN 1000 + 10
# define MAXM 5000 + 10
# define INF 0x3f3f3f3f
using namespace std;

int t, s, d, cnt;
int vis[MAXN];
int head[MAXN];
int dist[MAXN];

struct node
{
	int from, to, val, next;
	
}p[MAXM];

void add(int u, int v, int w)
{
	p[cnt].from = u;
	p[cnt].to = v;
	p[cnt].val = w;
	p[cnt].next = head[u];
	head[u] = cnt++;
}

void get()
{
	int a, b, c;
	while(t--)
	{
		scanf("%d%d%d",&a,&b,&c);
		add(a, b, c);
		add(b, a, c);
	}
}

void init()
{
	cnt = 0;
	memset(head, -1, sizeof(head));
	memset(vis, 0, sizeof(head));
}

void spfa(int px)
{
	vis[px] = 1;
	dist[px] = 0;
	queue<int>Q;
	Q.push(px);
	
	while(!Q.empty())
	{
		int u = Q.front();
		Q.pop();
		vis[u] = 0;
		for(int i = head[u]; i != -1; i = p[i].next)
		{
			int v = p[i].to;
			if(dist[v] > dist[u] + p[i].val)
			{
				dist[v] = dist[u] + p[i].val;
				if(!vis[v])
				{
					vis[v] = 1;
					Q.push(v);
				}
			}
		}
	}
}
int main()
{
	while(scanf("%d%d%d",&t,&s,&d)!=EOF)
	{
		init();
		get();
		int r, x, time;
		while(s--)
		{
			scanf("%d",&x);
			add(0, x, 0);
			add(x, 0, 0);//比较纠结的地方在这里,将x点与源点的距离赋值为0
		}
		for(int i = 1; i <= 1000; i++)
		{
			dist[i] = INF;
		}
		spfa(0);
		time = INF;
		while(d--)
		{
			scanf("%d",&r);
			if(time > dist[r])
			 time = dist[r];
		}
		printf("%d\n",time);
	}
	return 0;
}
刚刚看到博友的文章, 看完以后真真有了醍醐灌顶的感觉。 如果对你们理解SFPA算法也有帮助,那真是再好不过过了、

如何求得最短路径的长度值?

首先说明,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|次,那么就可以断定图中存在负权回路了。

我对于存在负权回路的理解是若有一条负权边,则最短距离会变成无穷小,也就是这个算法会陷入死循环中,那么必有一个结点循环次数超过了|V|,那么在循环结构加上一句判断语句就OK了(num【i】为循环次数的数组 )即num>|V| return ;

另外,给大家推荐一个网站,上面的资源很全的
链接 : http://mooc.guokr.com/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值