POJ 3259 Wormholes【最短路/SPFA判斷負環模板】
阿新 • • 發佈:2018-08-09
改變 air ont 數組 算法 他會 HERE push_back init
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.
農夫約翰在探索他的許多農場,發現了一些驚人的蟲洞。蟲洞是很奇特的,因為它是一個單向通道,可讓你進入蟲洞的前達到目的地!他的N(1≤N≤500)個農場被編號為1..N,之間有M(1≤M≤2500)條路徑,W(1≤W≤200)個蟲洞。FJ作為一個狂熱的時間旅行的愛好者,他要做到以下幾點:開始在一個區域,通過一些路徑和蟲洞旅行,他要回到最開時出發的那個區域出發前的時間。也許他就能遇到自己了:)。為了幫助FJ找出這是否是可以或不可以,他會為你提供F個農場的完整的映射到(1≤F≤5)。所有的路徑所花時間都不大於10000秒,所有的蟲洞都不大於萬秒的時間回溯。Input第1行:一個整數F表示接下來會有F個農場說明。 每個農場第一行:分別是三個空格隔開的整數:N,M和W 第2行到M+1行:三個空格分開的數字(S,E,T)描述,分別為:需要T秒走過S和E之間的雙向路徑。兩個區域可能由一個以上的路徑來連接。 第M +2到M+ W+1行:三個空格分開的數字(S,E,T)描述蟲洞,描述單向路徑,S到E且回溯T秒。OutputF行,每行代表一個農場 每個農場單獨的一行,” YES”表示能滿足要求,”NO”表示不能滿足要求。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.
#include<cstdio> #include<string> #include<cstdlib> #include<cmath> #include<iostream> #include<cstring> #include<set> #include<queue> #include<algorithm> #include<vector> #include<map> #include<cctype> #includeView Code<stack> #include<sstream> #include<list> #include<assert.h> #include<bitset> #include<numeric> #define debug() puts("++++") #define gcd(a,b) __gcd(a,b) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define fi first #define se second #define pb push_back #define sqr(x) ((x)*(x)) #define ms(a,b) memset(a,b,sizeof(a)) #define sz size() #define be begin() #define mp make_pair #define pu push_up #define pd push_down #define cl clear() #define lowbit(x) -x&x #define all 1,n,1 #define rep(i,x,n) for(int i=(x); i<=(n); i++) #define in freopen("in.in","r",stdin) #define out freopen("out.out","w",stdout) using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> P; const int INF = 0x3f3f3f3f; const LL LNF = 1e18; const int maxn = 1e5+20; const int maxm = 1e6 + 10; const double PI = acos(-1.0); const double eps = 1e-8; const int dx[] = {-1,1,0,0,1,1,-1,-1}; const int dy[] = {0,0,1,-1,1,-1,1,-1}; int dir[4][2] = {{0,1},{0,-1},{-1,0},{1,0}}; const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int tot,n,m,x,s; int u,v,w; int dis[maxn]; int cnt[maxn],vis[maxn]; struct cmp { bool operator()(int a,int b) { return dis[a] > dis[b]; } }; int head[maxn]; struct node { int v,w,nxt; }e[maxn]; void init() { tot=0; ms(cnt,0); ms(head,-1); ms(dis,INF);//求最長路徑開始設為0 ms(vis,0); } void add(int u,int v,int w) { e[tot].v=v; e[tot].w=w; e[tot].nxt=head[u]; head[u]=tot++; } int spfa(int s) { queue<int> q; vis[s]=1; dis[s]=0; cnt[s]++; q.push(s); while(!q.empty()) { int u = q.front(); q.pop(); vis[u]=0; // for(int i=head[u];i!=-1;i=e[i].nxt) { int v = e[i].v; if(dis[v] > dis[u] + e[i].w) { dis[v] = dis[u] + e[i].w; if(!vis[v]) { vis[v]=1; q.push(v); if(++cnt[v]>n) return 1;//有負環 } } } } return 0; } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&m,&w); init(); int a,b,c; for(int i=1;i<=m;i++) { scanf("%d%d%d",&a,&b,&c); add(a,b,c);add(b,a,c); } for(int i=1;i<=w;i++) { scanf("%d%d%d",&a,&b,&c); add(a,b,-c); } if(spfa(1)) puts("YES"); else puts("NO"); } } /* 【題意】 【類型】 SPFA判斷負環 【分析】 spfa算法 我們都知道spfa算法是對bellman算法的優化,那麽如何用spfa算法來判斷負權回路呢?我們考慮一個節點入隊的條件是什麽,只有那些在前一遍松弛中改變了距離估計值的點,才可能引起他們的鄰接點的距離估計值的改變。因此,用一個先進先出的隊列來存放被成功松弛的頂點。同樣,我們有這樣的定理:“兩點間如果有最短路,那麽每個結點最多經過一次。也就是說,這條路不超過n-1條邊。”(如果一個結點經過了兩次,那麽我們走了一個圈。如果這個圈的權為正,顯然不劃算;如果是負圈,那麽最短路不存在;如果是零圈,去掉不影響最優值)。也就是說,每個點最多入隊n-1次(這裏比較難理解,需要仔細體會,n-1只是一種最壞情況,實際中,這樣會很大程度上影響程序的效率)。 有了上面的基礎,思路就很顯然了,加開一個數組記錄每個點入隊的次數(num),然後,判斷當前入隊的點的入隊次數,如果大於n-1,則說明存在負權回路。 【時間復雜度&&優化】 【trick】 【數據】 */
POJ 3259 Wormholes【最短路/SPFA判斷負環模板】