POJ 3259 Wormholes(bellman_ford,判断有没有负环回路)
题意:John的农场里field块地,path条路连接两块地,hole个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。我们的任务是知道会不会在从某块地出发后又回来,看到了离开之前的自己。
思路:
这题就是判断存不存在负环回路。
前M条是双向边,后面的W是单向的负边。
为了防止出现不连通,增加一个结点作为起点。起点到所有点的长度为0
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <vector> using namespace std; /* * 单源最短路bellman_ford算法,复杂度O(VE) * 可以处理负边权图。 * 可以判断是否存在负环回路。返回true,当且仅当图中不包含从源点可达的负权回路 * vector<Edge>E;先E.clear()初始化,然后加入所有边 * 点的编号从1开始 */ 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; bool bellman_ford(int start, int n)//点的编号从1开始 { for (int i = 1; i <= n; i++)dist[i] = INF; dist[start] = 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;//第n次更新则有负环回路 return true;//没有负环回路 } int main() { int T; int N, M, W; int a, b, c; scanf("%d", &T); while (T--) { scanf("%d%d%d", &N, &M, &W); E.clear(); while (M--) { scanf("%d%d%d", &a, &b, &c); E.push_back(Edge(a, b, c)); E.push_back(Edge(b, a, c)); } while (W--) { scanf("%d%d%d", &a, &b, &c); E.push_back(Edge(a, b, -c)); } for (int i = 1; i <= N; i++) E.push_back(Edge(N + 1, i, 0)); if (!bellman_ford(N + 1, N + 1))printf("YES\n"); else printf("NO\n"); } return 0; }
(2) 如果一开始对所有顶点i,都把dist[i]初始化为0,那么可以检查出所有的负圈
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <vector> using namespace std; /* * 单源最短路bellman_ford算法,复杂度O(VE) * 可以处理负边权图。 * vector<Edge>E;先E.clear()初始化,然后加入所有边 * 点的编号从1开始 */ 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; bool bellman_ford(int n)//点的编号从1开始 { for (int i = 1; i <= n; i++)dist[i] = 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;//第n次更新则有负环回路 return true;//没有负环回路 } int main() { int T; int N, M, W; int a, b, c; scanf("%d", &T); while (T--) { scanf("%d%d%d", &N, &M, &W); E.clear(); while (M--) { scanf("%d%d%d", &a, &b, &c); E.push_back(Edge(a, b, c)); E.push_back(Edge(b, a, c)); } while (W--) { scanf("%d%d%d", &a, &b, &c); E.push_back(Edge(a, b, -c)); } if (!bellman_ford(N))printf("YES\n"); else printf("NO\n"); } return 0; }
(3) SPFA
某个顶点进入队列的次数超过N,则有负环
#include <iostream> #include <string.h> #include <stdio.h> #include <algorithm> #include <vector> #include <queue> using namespace std; /* * 单源最短路SPFA */ const int MAXN = 1010; const int INF = 0x3f3f3f3f; struct Edge { int v; int cost; Edge(int _v = 0, int _cost = 0) :v(_v), cost(_cost){} }; vector<Edge> E[MAXN]; void addedge(int u, int v, int w) { E[u].push_back(Edge(v, w)); } bool vis[MAXN]; int cnt[MAXN]; int dist[MAXN]; bool SPFA(int start, int n) { memset(vis, false, sizeof(vis)); for (int i = 1; i <= n; i++)dist[i] = INF; dist[start] = 0; vis[start] = true; queue<int>que; while (!que.empty())que.pop(); que.push(start); memset(cnt, 0, sizeof(cnt)); cnt[start] = 1; while (!que.empty()) { int u = que.front(); que.pop(); vis[u] = false; for (int i = 0; i<E[u].size(); i++) { int v = E[u][i].v; if (dist[v]>dist[u] + E[u][i].cost) { dist[v] = dist[u] + E[u][i].cost; if (!vis[v]) { vis[v] = true; que.push(v); if (++cnt[v]>n)return false; //有负环回路 } } } } return true; } int main() { int T; int N, M, W; int a, b, c; scanf("%d", &T); while (T--) { scanf("%d%d%d", &N, &M, &W); for (int i = 1; i <= N + 1; i++)E[i].clear(); while (M--) { scanf("%d%d%d", &a, &b, &c); addedge(a, b, c); addedge(b, a, c); } while (W--) { scanf("%d%d%d", &a, &b, &c); addedge(a, b, -c); } for (int i = 1; i <= N; i++) addedge(N + 1, i, 0); if (!SPFA(N + 1, N + 1))printf("YES\n"); else printf("NO\n"); } return 0; }