POJ3259 判断是否存在负权环

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(1N500) fields conveniently numbered 1..N,M(1M2500) paths, and W(1W200) 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(1F5) 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 4M+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.


如果你想要翻译的话 :

题目描述

    John 的农场里 N(N < = 500)块地,M(M <= 2500)条双向路分别连接两块地,有 W(W <= 200)个时空隧道,时空隧道是一条单向路,当你走完一条时空隧道的时候时间会倒退 T 秒。我们的任务是想知道 John 能不能从某块地出发后又回来,使得他回来的时间早于出发的时间,就是说可能在自己从起点出发之前就回来了(时空逆转)。

输入格式

    第一行:一个正整数 x,表示有 x (1x5) 组数据。每组数据格式如下:
    第 1 行:三个正整数,即 NMW
    接下来有 M 行,每行三个正整数 S,E,T0 ,表示 S E 有路相连(双向),走完这条路需要 T0 (0<T010000) 秒的时间。任意两块地之间可能有多条路相连。
    接下来有 W 行,每行三个正整数 S,E,T1 ,表示从 S E 有时空隧道(单向),当走完这条路的时候时间会倒退 T10<T110000) 秒。

输出格式

    对于每组测试数据,输出一个单词,如果 John 能够成功,输出“YES”,否则输出“NO”。

备注

[样例说明]
第一组样例:John不能早于出发时间回到出发位置.
第二组样例:John能够在他离开前1秒,通过循环回到出发位置: 1231 ,他可以从任何地方开始去完成这一切。



BFS-SPFA

    网上写的大多的 BFS-SPFA 其实跟我们平时写的 SPFA 是相似的, 所谓加上 BFS 用来区分 DFS-SPFA,其实 SPFA 常见的实现方式就是 BFS 的想法,所以我这里不多叙述。BFS-SPFA 判负环的思想 : 一个的点入栈大 n 次,那么图一定存在负环。但是这种方法并不推荐,因为你的一个的点入栈大于 n 次,那么其环上的点一定也有 n - 1次入栈,这样你的环大小不确定,复杂度不敢保证,况且裸的 SPFA 很好卡 HDU 上有道题就是和卡 SPFA 有关的, 有兴趣的话可以去看看。

    我这里就给个伪代码。手打并没有编译。

inline void BFS_DFS(int s) {
    for(int i = 1; i <= n; ++i) dis[i] = num[i] = instack[i] = 0;
    dis[s] = 0; instack[s] = true; ++num[s];
    queue<int> q; q.push(s);
    while(!q.empty()) {
        int u = q.front(); q.pop(); instack[u] = false;
        for(int i = first[u]; i; i = nxt[i]) 
            if(dis[to[i] > dis[u] + len[i]) {
                dis[to[i]] = dis[u] + len[i];
                if(!instack[to[i]]) {
                    ++num[to[i]]; q.push(to[i]);
                    if(num[to[i]] > n) { flag = 1; return; }
                }
            }
    }
}

inline void judge() {
    if(flag) printf("YES\n");
    else printf("NO\n");
}



DFS-SPFA

    DFS-SPFA 在网上大多数都是判断一个点是否在一条路径上存在多次,当然在写法就是判断当前如要被更新并且还在栈中就可以判断图中存在负环,其写法和 dij 有点相同,但是这样的话我扫了很多没有用的边,于是我们考虑优化。
    我们可以将每个点的 dis 初值都赋成 0, 我们对每个的点都进行 DFS, 在所有的起始点我们都只遍历边权为负的边, 因为每个的点的 dis 初始都是 0, 最先找到的边是负权,接着往深处遍历的时候,只找和为负权的路径,这样,我们的 DFS 只会遍历负权路径,有很多的边我们就优化掉了。

    这里给出此题的代码,在 POJ 上加上普通的就可以跑 16 ms,还是比较好些。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define clr(a) memset(a, 0, sizeof(a))
using namespace std;

inline int read() {
    int i = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1; ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        i = (i << 3) + (i << 1) + ch - '0'; ch = getchar();
    }
    return i * f;
}

const int MAXN = 500 + 5;
const int MAXM = 3000 + 5;
int first[MAXN], nxt[MAXM * 2], to[MAXM * 2], len[MAXM * 2];
int tot, dis[MAXN], vis[MAXN], flag;

inline void addedge(int x, int y, int w) {
    nxt[++tot] = first[x]; first[x] = tot; to[tot] = y, len[tot] = w;
}

inline void Dfs_spfa(int x) {
    vis[x] = 1;
    for(int i = first[x]; i; i = nxt[i]) {
        int v = to[i]; if(flag) return ;
        if(dis[v] > dis[x] + len[i]) {
            dis[v] = dis[x] + len[i];
            if(vis[v]) {
                flag = 1;
                return;
            }
            else 
                Dfs_spfa(v);
        }
    }
    vis[x] = 0;
}

int main() {
    int t = read();
    while(t--) {
        clr(dis), clr(vis), clr(first); clr(nxt);tot = 0;
        int n = read(), m = read(), w = read();
        for(int i = 1; i <= m; ++i) {
            int x = read(), y = read(), z = read();
            addedge(x, y, z);
            addedge(y, x, z);
        }
        for(int i = 1; i <= w; ++i) {
            int x = read(), y = read(), z = read();
            addedge(x, y, -z);
        }
        flag = 0;
        for(int i = 1; i <= n; ++i) {
            if(flag) break;
            Dfs_spfa(i);
        }
        if(flag) printf("YES\n");
        else printf("NO\n");
    }
}

你可能感兴趣的:(POJ,图论,SPFA)