POJ 3259 Wormholes (spfa--最好用最快 判断是否存在负权回路)ʕ •ᴥ•ʔ

spfa的算法思想(动态逼近法):
    设立一个先进先出的队列q用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。 
    松弛操作的原理是著名的定理:“三角形两边之和大于第三边”,在信息学中我们叫它三角不等式。所谓对结点i,j进行松弛,就是判定是否dis[j]>dis[i]+w[i,j],如果该式成立则将dis[j]减小到dis[i]+w[i,j],否则不动。 
    下面举一个实例来说明SFFA算法是怎样进行的:



和广搜bfs的区别:
    SPFA 在形式上和广度(宽度)优先搜索非常类似,不同的是bfs中一个点出了队列就不可能重新进入队列,但是SPFA中一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本身被改进(重新入队),于是再次用来改进其它的点,这样反复迭代下去。

 

Wormholes

Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 29971 Accepted: 10844

 

Description

While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way path that delivers you to its destination at a time that is BEFORE you entered the wormhole! Each of FJ's farms comprises N (1 ≤ N ≤ 500) fields conveniently numbered 1..NM (1 ≤ M ≤ 2500) paths, and W (1 ≤ W ≤ 200) wormholes.

As FJ is an avid time-traveling fan, he wants to do the following: start at some field, travel through some paths and wormholes, and return to the starting field a time before his initial departure. Perhaps he will be able to meet himself :) .

To help FJ find out whether this is possible or not, he will supply you with complete maps to F (1 ≤ F ≤ 5) of his farms. No paths will take longer than 10,000 seconds to travel and no wormhole can bring FJ back in time by more than 10,000 seconds.

Input

Line 1: A single integer, FF farm descriptions follow. 
Line 1 of each farm: Three space-separated integers respectively: NM, and W 
Lines 2..M+1 of each farm: Three space-separated numbers (SET) that describe, respectively: a bidirectional path between S and E that requires T seconds to traverse. Two fields might be connected by more than one path. 
Lines M+2..M+W+1 of each farm: Three space-separated numbers (SET) that describe, respectively: A one way path from S to E that also moves the traveler back T seconds.

Output

Lines 1..F: For each farm, output "YES" if FJ can achieve his goal, otherwise output "NO" (do not include the quotes).

Sample Input

2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8

Sample Output

NO
YES

Hint

For farm 1, FJ cannot travel back in time. 
For farm 2, FJ could travel back in time by the cycle 1->2->3->1, arriving back at his starting location 1 second before he leaves. He could start from anywhere on the cycle to accomplish this.

Source

USACO 2006 December Gold

 

解题思路:

农民有N块地,每块地看做一个节点,有m条普通的路(双向),连接着两个节点,从一端走到另一端需要w时间(权值),还有wh条特殊的单向路,也就是题意中的虫洞,虫洞也连接着两个节点,但虫洞是单向的,从起点走到终点需要w时间,但这个时间是负的,也就是题意中所说的时光倒流,比如 一个虫洞连接着s -> e,在s处假设时间为6,走虫洞时间为4,那么走过去时光倒流,走到e时时间就变为了2 (6 -4,也就是虫洞这条路的权值为 -4 ) ,好神奇。。。。问有没有这样一种情况,就是第二次走到某个节点的时间比第一次走到该节点所用的时间短(题中的Perhaps he will be able to meet himself,时光倒流的作用)。假设存在这种情况,那么以某节点为起点和终点一定存在着一个回路,这个回路的权值是负的,这样第二次所用的时间一定比第一次少。

个人理解:

spfa算法和dijkstra算法 非常相似 可以说是优化了的dijkstra 用queue 队列降低了时间复杂度 而且解决了dijkstra 不能录入负边权的问题。dijkstra算法 “目光短浅” 没办法看到“远方”的负边

 代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#define N 0x3f3f3f3f
using namespace std;
int map[550][550];
int vis[550],s[550];//vis 记录当前这个点是否在队列里面 s 记录路径 
int num[550];//记录当前点入队多少次。 
int n,m,q;
int spfa()
{
	memset(vis,0,sizeof(vis));
	memset(s,N,sizeof(s));
	memset(num,0,sizeof(num));
	s[1]=0;
	vis[1]=1;
	num[1]=1;
	queue<int>q;
	q.push(1);
	while(q.size())
	{
		int t=q.front();
		q.pop();
		vis[t]=0;//拿出之后就释放 
		for(int i=1;i<=n;i++)
		{
			if(s[t]+map[t][i]<s[i])
			{
				s[i]=s[t]+map[t][i];
				if(vis[i]==0)
				{
					vis[i]=1;
					num[i]++;
					if(num[i]>n)
					{
						return 1;
					}
					q.push(i);
				}
			}
		}		
	}
	return 0;
	
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		memset(map,N,sizeof(map));	
		cin>>n>>m>>q;
		for(int i=1;i<=m;i++)
		{
			int a,b,c;
			cin>>a>>b>>c;
			if(map[a][b]>c)
			map[a][b]=map[b][a]=c;
		}
		for(int i=1;i<=q;i++)
		{
			int a,b,c;
			cin>>a>>b>>c;
			if(map[a][b]>-c)
			map[a][b]=-c;
		}
	if(spfa())
	cout<<"YES"<<endl;
	else
	cout<<"NO"<<endl;
		
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值