1. 程式人生 > >POJ 3259 Wormholes (spfa--最好用最快 判斷是否存在負權迴路)ʕ •ᴥ•ʔ

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

解題思路:

農民有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;
}