地址 https://www.acwing.com/problem/content/description/906/
农夫约翰在巡视他的众多农场时,发现了很多令人惊叹的虫洞。 虫洞非常奇特,它可以看作是一条 单向 路径, 通过它可以使你回到过去的某个时刻(相对于你进入虫洞之前)。 农夫约翰的每个农场中包含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≤F≤5 1≤N≤500, 1≤M≤2500, 1≤W≤200, 1≤T≤10000, 1≤S,E≤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
解答
由于虫洞是回到之前 所以可以看做是田地为点的负权值边。
田地之间是双向边。
只要检测到图中存在负环,那么就可以无限穿越到之前的时间,而由于田地点之间是双向边,所以肯定能到达任意一个田地(也包括起点)。所以本题目就是判断图中是否存在负环。
是一个spfa判断负环的模板题目
// 1231234.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <iostream> #include <queue> #include <vector> #include <memory.h> using namespace std; int loop; const int N = 510; int n, m, w; vector<pair<int, int>> g[N]; bool st[N]; int dist[N]; int cnt[N]; bool spfa() { queue<int> q; for (int i = 1; i <= n; i++) { st[i] = true; q.push(i); } while (q.size()) { int t = q.front(); q.pop(); st[t] = false; for (int i = 0; i < g[t].size(); i++) { int j = g[t][i].first; int w = g[t][i].second; if (dist[j] > dist[t] + w) { dist[j] = dist[t] + w; cnt[j] = cnt[t] + 1; if (cnt[j] >= n) return true; if (!st[j]) { q.push(j); st[j] = true; } } } } return false; } int main() { cin >> loop; while (loop--) { //多样例 初始化 memset(st, 0, sizeof st); memset(dist, 0, sizeof dist); memset(cnt, 0, sizeof cnt); for (int i = 0; i < N; i++) g[i].clear(); cin >> n >> m >> w; for (int i = 0; i < m; i++) { int a, b, c; cin >> a >> b >> c; g[a].push_back({ b,c }); g[b].push_back({ a,c }); } for (int i = 0; i < w; i++) { int a, b, c; cin >> a >> b >> c; g[a].push_back({ b,-c }); } if (spfa()) cout << "YES" << endl; else cout << "NO" << endl; } return 0; }