1 条题解
-
0
农场时间旅行问题题解
一、题目分析
题目要求判断农场中是否存在时间回路,即从某块田地出发,经过若干路径和虫洞后回到起点时的时间早于出发时间。关键条件:
- 路径是双向的,虫洞是单向的且会回溯时间;
- 需判断是否存在负权回路(时间减少的回路)。
二、算法思路
-
负权回路检测:
- 虫洞的时间回溯相当于负权边(时间减少);
- 使用Bellman-Ford算法检测图中是否存在负权回路。
-
Bellman-Ford算法:
- 初始化距离数组,源点距离为0,其他为无穷大;
- 进行n-1次松弛操作(n为节点数);
- 若第n次松弛仍能更新距离,说明存在负权回路。
三、代码实现
#include <iostream> #include <cstring> #include <stdio.h> #include <algorithm> #include <vector> using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 550; int dist[MAXN]; // 距离数组 struct Edge { int u, v; // 边的起点和终点 int cost; // 边的权值(时间) Edge(int _u = 0, int _v = 0, int _cost = 0) : u(_u), v(_v), cost(_cost) {} }; vector<Edge> E; // 存储所有边 // Bellman-Ford算法检测负权回路 bool bellman_ford(int start, int n) { for (int i = 1; i <= n; i++) dist[i] = INF; dist[start] = 0; // 源点距离初始化为0 for (int i = 1; i < n; i++) { // 最多松弛n-1次 bool flag = false; for (int j = 0; j < E.size(); j++) { int u = E[j].u; int v = E[j].v; int cost = E[j].cost; if (dist[v] > dist[u] + cost) { // 松弛操作 dist[v] = dist[u] + cost; flag = true; } } if (!flag) return true; // 没有松弛,提前终止 } // 检查是否存在负权回路 for (int j = 0; j < E.size(); j++) if (dist[E[j].v] > dist[E[j].u] + E[j].cost) return false; // 存在负权回路 return true; // 不存在负权回路 } int main() { int t; cin >> t; while (t--) { E.clear(); // 清空边集合 int n, m, w; cin >> n >> m >> w; // 读取双向路径 for (int j = 0; j < m; j++) { int u, v, cost; cin >> u >> v >> cost; E.push_back(Edge(u, v, cost)); E.push_back(Edge(v, u, cost)); // 双向边 } // 读取虫洞(单向负权边) for (int j = 0; j < w; j++) { int u, v, cost; cin >> u >> v >> cost; E.push_back(Edge(u, v, -cost)); // 虫洞回溯时间,转化为负权边 } // 检测是否存在负权回路 if (!bellman_ford(1, n)) { cout << "YES" << endl; } else { cout << "NO" << endl; } } return 0; }
四、代码解释
-
输入处理:
- 读取农场数量t,逐个处理每个农场;
- 路径是双向的,添加两条方向相反的边;
- 虫洞是单向的,添加一条负权边(时间回溯转化为负权)。
-
Bellman-Ford算法:
- 初始化距离数组,源点(田地1)距离为0;
- 进行n-1次松弛,若某轮无更新则提前终止;
- 最后检查是否存在可松弛的边,存在则有负权回路。
-
结果判断:
- 若存在负权回路,输出"YES",否则输出"NO"。
五、示例解析
输入样例2:
3 2 1 1 2 3 2 3 4 3 1 8
-
边构建:
- 路径1-2(3)、2-1(3);
- 路径2-3(4)、3-2(4);
- 虫洞3-1(-8)。
-
松弛过程:
- 初始dist[1]=0,其他为INF;
- 第1次松弛:dist[2]=3,dist[3]=7(1→2→3);
- 第2次松弛:dist[1]=7+(-8)=-1(3→1);
- 第3次检查:dist[1]可通过3→1进一步松弛,存在负权回路。
-
输出:
YES
六、复杂度分析
- 时间复杂度:O(F×(N×M)),其中F为农场数,N为节点数,M为边数;
- 空间复杂度:O(M),存储边集合。
七、关键技巧
- 负权边转换:虫洞的时间回溯转化为负权边;
- Bellman-Ford应用:利用算法检测负权回路,适用于存在负权边的图;
- 提前终止优化:若某轮松弛无更新,提前终止循环。
- 1
信息
- ID
- 2260
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者