spfa算法是对bellman算法的优化,然鹅找了很多模板大部分都很混乱,终于碰着一个很干净整洁的模板,转载辽:
原文网址:http://www.cnblogs.com/zhengguiping--9876/p/4797195.html
我们都知道spfa算法是对bellman算法的优化,那么如何用spfa算法来判断负权回路呢?
我们考虑一个节点入队的条件是什么,只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。因此,用一个先进先出的队列来存放被成功松弛的顶点。
同样,我们有这样的定理:“两点间如果有最短路,那么每个结点最多经过一次。也就是说,这条路不超过n-1条边。”(如果一个结点经过了两次,那么我们走了一个圈。如果这个圈的权为正,显然不划算;如果是负圈,那么最短路不存在;如果是零圈,去掉不影响最优值)。也就是说,每个点最多入队n-1次(这里比较难理解,需要仔细体会,n-1只是一种最坏情况,实际中,这样会很大程度上影响程序的效率)。
有了上面的基础,思路就很显然了,加开一个数组记录每个点入队的次数(num),然后,判断当前入队的点的入队次数,如果大于n-1,则说明存在负权回路。
【最可贵的是两份代码都有,是同一个模板,两个算法差别在哪一看便知。并且使用了结构体,没有出现一堆一堆的数组,极度舒适了可以说~】
BellmanFord:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <math.h>
#include <queue>
#include <algorithm>
using namespace std;
#define N 5210
#define INF 0xfffffff
int cnt, dist[N], Head[N];
int n, m, w;
struct Edge{
int u, v, w, next;
}e[N];
void Add(int u, int v, int w){
e[cnt].u = u;
e[cnt].v = v;
e[cnt].w = w;
e[cnt].next = Head[u];
Head[u] = cnt++;
}
bool BellmanFord(){
dist[1] = 0;
for(int i=0; i<n; i++){
for(int j=0; j<cnt; j++){
if(dist[e[j].v] > dist[e[j].u]+e[j].w)
dist[e[j].v] = dist[e[j].u]+e[j].w;
}
}
for(int i=0; i<cnt; i++){
if(dist[e[i].v] > dist[e[i].u]+e[i].w)
return 0;
}
return 1;
}
int main(){
int T, a, b, c;
scanf("%d", &T);
while(T--){
scanf("%d%d%d", &n, &m, &w);
cnt = 0;
memset(Head, -1, sizeof(Head));
for(int i=1; i<=n; i++)
dist[i] = INF;
for(int i=1; i<=m; i++){
scanf("%d%d%d", &a, &b, &c);
Add(a, b, c);
Add(b, a, c);
}
for(int i=1; i<=w; i++){
scanf("%d%d%d", &a, &b, &c);
Add(a, b, -c);
}
if( !BellmanFord() )
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
Spfa:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <math.h>
#include <queue>
#include <algorithm>
using namespace std;
#define N 5210
#define INF 0xfffffff
int cnt, dist[N], Head[N], num[N], vis[N];
int n, m, w;
struct Edge{
int v, w, next;
}e[N];
void Add(int u, int v, int w){
e[cnt].v = v;
e[cnt].w = w;
e[cnt].next = Head[u];
Head[u] = cnt++;
}
bool spfa(){
memset(vis, 0, sizeof(vis));
memset(num, 0, sizeof(num));
queue<int>Q;
vis[1] = 1;
dist[1] = 0;
Q.push(1);
num[1]++;
while(Q.size()){
int p=Q.front();
Q.pop();
vis[p] = 0;
for(int i=Head[p]; i!=-1; i=e[i].next){
int q = e[i].v;
if(dist[q] > dist[p] + e[i].w){
dist[q] = dist[p] + e[i].w;
if(!vis[q]){
vis[q] = 1;
Q.push(q);
num[q] ++;
if(num[q]>n)
return true;
}
}
}
}
return false;
}
int main(){
int T, a, b, c;
scanf("%d", &T);
while(T--){
scanf("%d%d%d", &n, &m, &w);
cnt = 0;
memset(Head, -1, sizeof(Head));
for(int i=1; i<=n; i++)
dist[i] = INF;
for(int i=1; i<=m; i++){
scanf("%d%d%d", &a, &b, &c);
Add(a, b, c);
Add(b, a, c);
}
for(int i=1; i<=w; i++){
scanf("%d%d%d", &a, &b, &c);
Add(a, b, -c);
}
if( spfa() )///存在负环;
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
差分约束写的比较清晰的模板:https://blog.csdn.net/u012348973/article/details/78117300