负环(Negative Ring)

简介(Introduction)

一个图中,环的边权和 $ < 0$ ,进行无限次循环后,环上点的距离就会被更新成 \(-\infty\)



描述(Description)

  • 判断方式:
    1. 统计每个点入队的次数如果某个点入队 \(n\) 次说明存在负环
    2. 统计当前每个点的最短路中所包含的边数,如果某个点的最短路所包含的边数 \(\ge n\) 说明存在负环(常用)
  • 判读环出现 \(TLE\) 时:
    1. \(queue\) 换成 \(stack\)
    2. 当更新超过 \(10^4\) 方次数时,可以默认该图存在负环



示例(Example)

image



代码(Code)

// C++ Version

int dist[N], cnt[N];
bool st[N];
int q[N];

bool spfa() {
	int hh = 0, tt = 0;

	memset(dist, 0, sizeof dist);
	memset(st, 0, sizeof st);
	memset(cnt, 0, sizeof cnt);

	for (int i = 1; i <= n; i ++ ) {
		q[tt ++ ] = i;
		st[i] = true;
	}

	while (hh != tt) {
		int t = q[hh ++ ];
		if (hh == N) hh = 0;

		st[t] = false;

		for (int i = h[t]; ~i; i = ne[i]) {
			int j = e[i];
			if (dist[j] > dist[t] + w[i]) {
				dist[j] = dist[t] + w[i];
				cnt[j] = cnt[t] + 1;
				if (cnt[j] >= n) return true; // 抽屉原理
				if (!st[j]) {
					q[tt ++ ] = j;
					if (tt == N) tt = 0;
					st[j] = true;
				}
			}
		}
	}
	return false;
}



应用(Application)



虫洞


农夫约翰在巡视他的众多农场时,发现了很多令人惊叹的虫洞。

虫洞非常奇特,它可以看作是一条 单向 路径,通过它可以使你回到过去的某个时刻(相对于你进入虫洞之前)。

农夫约翰的每个农场中包含 \(N\) 片田地,\(M\) 条路径(双向)以及 \(W\) 个虫洞。

现在农夫约翰希望能够从农场中的某片田地出发,经过一些路径和虫洞回到过去,并在他的出发时刻之前赶到他的出发地。

他希望能够看到出发之前的自己。

请你判断一下约翰能否做到这一点。

下面我们将给你提供约翰拥有的农场数量 \(F\),以及每个农场的完整信息。

已知走过任何一条路径所花费的时间都不超过 \(10000\) 秒,任何虫洞将他带回的时间都不会超过 \(10000\) 秒。

输入格式

第一行包含整数 \(F\),表示约翰共有 \(F\) 个农场。

对于每个农场,第一行包含三个整数 \(N,M,W\)

接下来 \(M\) 行,每行包含三个整数 \(S,E,T\),表示田地 \(S\)\(E\) 之间存在一条路径,经过这条路径所花的时间为 \(T\)

再接下来 \(W\) 行,每行包含三个整数 \(S,E,T\),表示存在一条从田地 \(S\) 走到田地 \(E\) 的虫洞,走过这条虫洞,可以回到 \(T\) 秒之前。

输出格式

输出共 \(F\) 行,每行输出一个结果。

如果约翰能够在出发时刻之前回到出发地,则输出 YES,否则输出 NO

数据范围

\(1 \le F \le 5\)
\(1 \le N \le 500\),
\(1 \le M \le 2500\),
\(1 \le W \le 200\),
\(1 \le T \le 10000\),
\(1 \le S,E \le N\)

输入样例:

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

输出样例:

NO
YES
  • 题解:

    // C++ Version
    
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 510, M = 5210;
    
    int n, m1, m2;
    int h[N], e[M], ne[M], w[M], idx;
    int dist[N], cnt[N];
    bool st[N];
    int q[N];
    
    void add(int a, int b, int c) {
    	e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    }
    
    bool spfa() {
    	int hh = 0, tt = 0;
    
    	memset(dist, 0, sizeof dist);
    	memset(st, 0, sizeof st);
    	memset(cnt, 0, sizeof cnt);
    
    	for (int i = 1; i <= n; i ++ ) {
    		q[tt ++ ] = i;
    		st[i] = true;
    	}
    
    	while (hh != tt) {
    		int t = q[hh ++ ];
    		if (hh == N) hh = 0;
    
    		st[t] = false;
    
    		for (int i = h[t]; ~i; i = ne[i]) {
    			int j = e[i];
    			if (dist[j] > dist[t] + w[i]) {
    				dist[j] = dist[t] + w[i];
    				cnt[j] = cnt[t] + 1;
    				if (cnt[j] >= n) return true;
    				if (!st[j]) {
    					q[tt ++ ] = j;
    					if (tt == N) tt = 0;
    					st[j] = true;
    				}
    			}
    		}
    	}
    	return false;
    }
    
    int main() {
    	int T;
    	cin >> T;
    	while (T -- ) {
    		cin >> n >> m1 >> m2;
    		memset(h, -1, sizeof h);
    		idx = 0;
    
    		while (m1 -- ) {
    			int a, b, c;
    			cin >> a >> b >> c;
    			add(a, b, c), add(b, a, c);
    		}
    
    		while (m2 -- ) {
    			int a, b, c;
    			cin >> a >> b >> c;
    			add(a, b, -c);
    		}
    
    		if (spfa()) puts("YES");
    		else puts("NO");
    	}
    	return 0;
    }
    

posted @ 2023-05-16 14:43  FFex  阅读(5)  评论(0编辑  收藏  举报