最短路的Bellman-Ford算法 【判断有无负权环】
2014-08-20 22:15
309 查看
Bellman-Ford算法是一种求单源最短路算法,时间复杂度:O(V * E)(V为图的节点数,E为图的边数),效率很低,但比起dijkstra算法,它可以处理负权边,而且能判断源点是否有负权环(floyd算法只能求最短路不能判断有无),即从源点经过一段路后回到原点有无负权和。
过程:
for(节点数-1) // 跟 dijkstra一样
对每条边松弛
for(每条边)
if(可以松弛)
return 存在负边
return 无负边
常用一个数组dis[]表示源点到各点距离
结构体{
int u; // 起点
int v; // 终点
int w; // 边权
}edge[]表示边的信息
松弛部分很好理解,源点到某一边的终点距离 小于 源点到该边的起点距离 + 起点到终点距离 就更新 到终点距离
下面看POJ 3259 例题
题意:有F个农场(测试数据组数),N个田(节点),M条双向路径(双/无向正权边),W个虫洞(单向负权边),问能否遇见原先的自己(负权环)
这题输入部分
此题的spfa解法
过程:
BELLMAN-FORD(G, w, s) 1 INITIALIZE-SINGLE-SOURCE(G, s) 2 for i ← 1 to |V[G]| - 1 3 do for each edge (u, v) ∈ E[G] 4 do RELAX(u, v, w) 5 for each edge (u, v) ∈ E[G] 6 do if d[v] > d[u] + w(u, v) 7 then return FALSE 8 return TRUE
for(节点数-1) // 跟 dijkstra一样
对每条边松弛
for(每条边)
if(可以松弛)
return 存在负边
return 无负边
常用一个数组dis[]表示源点到各点距离
结构体{
int u; // 起点
int v; // 终点
int w; // 边权
}edge[]表示边的信息
松弛部分很好理解,源点到某一边的终点距离 小于 源点到该边的起点距离 + 起点到终点距离 就更新 到终点距离
if( dis[edge[j].u] + edge[j].w < dis[edge[j].v] ) { dis[edge[j].v] = dis[edge[j].u] + edge[j].w; }
下面看POJ 3259 例题
题意:有F个农场(测试数据组数),N个田(节点),M条双向路径(双/无向正权边),W个虫洞(单向负权边),问能否遇见原先的自己(负权环)
这题输入部分
输入F; while(F--) { 输入N,M,W; 1 -> M 加边入图(双向边就当成两条边处理) 1 -> W 负权边入图(注意负权) 进入算法 }还可以用标记变量优化,见代码
#include<cstdio> #include<cmath> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define ll long long #define NMAX 2502 #define INF 0x7F7F7F7F #define eps 10^(-6) #define MEM(a) memset(a,0,sizeof(a)); #define MEM_MAX(a) memset(a,INF,sizeof(a)); #define FOR(i,n) for(int i=0;i<n;i++) #define FIN freopen("in.txt","r",stdin); #define FOUT freopen("out.txt","w",stdout); struct node { int u,v,w; //边的信息 }edge[NMAX*2+201]; int dis[NMAX]; //源点到各点距离 int N,M,W; //N:节点数,M:正权双向边数,W:负权边数 int cnt; //总边数:不等于M+W,因为双向边要算两条边 int bellman_ford(int src) { for(int i=1;i<=N;i++) dis[i] = INF; dis[src] = 0; for(int i=1;i<N;i++) { bool flag = false; for(int j=1;j<=cnt-1;j++) { if( dis[edge[j].u] + edge[j].w < dis[edge[j].v] ) { dis[edge[j].v] = dis[edge[j].u] + edge[j].w; flag = true; } } if(!flag) //优化:如果没一条边更新,则最短路完成或有边不可达 break; } for(int i=1;i<=cnt-1;i++) if(dis[edge[i].u] + edge[i].w < dis[edge[i].v]) return 0; return 1; } int main() { int u,v,w,f,M,W; scanf("%d",&f); while(f--) { cnt = 1; scanf("%d%d%d",&N,&M,&W); for(int i=1;i<=M;i++) { scanf("%d%d%d",&u,&v,&w); edge[cnt].u = u; edge[cnt].v = v; edge[cnt++].w = w; edge[cnt].v = u; edge[cnt].u = v; edge[cnt++].w = w; } for(int i=1;i<=W;i++) { scanf("%d%d%d",&u,&v,&w); edge[cnt].u = u; edge[cnt].v = v; edge[cnt++].w = -w; //负权 } if(bellman_ford(1)) printf("NO\n"); else printf("YES\n"); } return 0; }
此题的spfa解法
#include <cstdio> #include <cstring> #include <string> #include <stack> #include <queue> #include <iostream> #include <algorithm> using namespace std; const int maxn = 505; const int maxm = 2600; const int inf = 0x3f3f3f3f; int inq[maxn], head[maxn], dis[maxn]; //inq[u]==1:u在队列里 struct Edge{ int v, w, next; } edge[maxm * 2]; int cnt; void add_edge(int u, int v, int w){ //邻接表前插法 edge[cnt].v = v; edge[cnt].w = w; edge[cnt].next = head[u]; head[u] = cnt++; } int times[maxn]; void init(int n){ cnt = 0; memset(head, -1, sizeof(head)); memset(inq, 0, sizeof(inq)); memset(dis, inf, sizeof(dis)); memset(times, 0, sizeof(times)); } int n; bool spfa(int s, int t){ queue<int>q; q.push(s); dis[s] = 0; inq[s] = 1; times[s]++; while (!q.empty()){ int u = q.front(); q.pop(); inq[u] = 0; for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; int w = edge[i].w; if (dis[v] > dis[u] + w){ dis[v] = dis[u] + w; if (!inq[v]){ inq[v] = 1; q.push(v); times[v]++; if(times[v] > n){ return false; } } } } } return true; } int main() { int m, a, b, c; int te; cin>>te; int qq; while(te--){ cin >> n >> m>>qq; init(n); for (int i = 0; i < m; ++i){ cin >> a >> b >> c; add_edge(a, b, c); add_edge(b, a, c); } for (int i = 0; i < qq; ++i){ cin >> a >> b >> c; add_edge(a, b, -c); } if(spfa(1, n)) cout<<"NO"<<endl; else cout<<"YES"<<endl; } return 0; }
相关文章推荐
- poj 3259 bellman最短路判断有无负权回路
- Wormholes( POJ 3259)(Bellman-Ford+SPFA)(判断是否有负权环)(最短路模板)
- poj 3259 bellman-ford算法 判断是否存在负权回路
- 单源最短路之bellman-ford算法(解决有负权存在的情况)
- poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)
- spfa 有无负权环 负权边最短路 模板
- POJ 3259 Wormholes (Bellman-Ford/SPFA 判断是否存在负权环)
- poj3259Wormholes(bellman判断负环的问题最短路)
- POJ3259(经典bellman判断负权)
- 最短路 bellman-ford算法
- 最短路-Bellman-Ford算法
- PKU3259(Wormholes)判定负权环-Bellman_Ford算法
- 最短路——Bellman-Ford算法
- POJ 3259 Wormholes (Bellman-Ford/SPFA 判断是否存在负权环)
- POJ 3259 Wormholes(Bellman-Ford判断是否有负权边)
- Bellman_Ford算法 - 解决负权边
- 最短路径之Bellman-Ford算法----解决负权边
- 最短路之bellman-ford算法
- POJ 3259 Wormholes (Bellman-Ford/SPFA 判断是否存在负权环)
- POJ-3259 Wormholes(最短路 Bellman-Ford算法)