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
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, F. F farm descriptions follow.
Line 1 of each farm: Three space-separated integers respectively: N, M, and W
Lines 2..M+1 of each farm: Three space-separated numbers (S, E, T) 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 (S, E, T) 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;
}