Bellman_ford算法,SPFA

Bellman_ford算法解决带负权边的带权有向图单源最短路问题。当图中不存在带负权的环时,可以求出单源最短路;当图中存在负权环时,可以判断出来;

时间复杂度:

邻接矩阵存图时 V^3;

邻接表存图时 V*E

算法思想:(设源点为s)

构造一个最短路径长度数组序列dist(1)[u],dist(2)[u],dist(3)[u],,,,,,,,dist(n-1)[u];其中:dist(k)[u]表示从源点s只经过k条边(不构成负权回路)的最短路径长度,则有dist(1)[u] = 边(s,u)权值,最终结果为dist(n-1)[u];

求dist(k)[u]:

(1)、当k = 1 时,dist(1)[u] = 边(s,u)权值;

(2)、假设已经求出了dist(k-1)[u](u = 0,1,,,,n-1);则用每一个和u相连的节点v(对于每条边(v,u)),计算出min(dist(k-1)[v] + 边(v,u)的权值为w;则dist(k) = min(dist(k-1)[u],w);

负权回路判断:求出dist(n-1)[u](u = 0,1,2,,,,n-1之后),在对每条边(x,y)判断一下:dist[x] + 边(x,y)的值 <dist[y];如果等式成立则有负权回路(n各节点的图中,某条路径上有n条边,则一定有回路);

poj 3259 Bellman _ford 模板题(邻接表存图);

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<cctype> 
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<string>
#include<stack>
#define ll long long
#define MAX 1000
#define INF 1000000000
#define eps 1e-8

using namespace std;

struct Edge{
	int from, to, dist;
};

vector<int>G[MAX];
vector<Edge>edges;

int n;

void init(){
	for (int i=0; i<=n; i++) G[i].clear();
	edges.clear();
}

void addEdge(int from, int to, int dist){
	edges.push_back((Edge){from,to,dist});
	int k = edges.size();
	G[from].push_back(k-1);
}

bool Bellman_ford(int s){
	int d[MAX];
	for (int i=0; i<n; i++) d[i] = (i == s ? 0 : INF);
	for (int i=1; i<n; i++){
		for (int j = 0; j<edges.size(); j++){
			Edge e = edges[j];
			if (d[e.from] < INF){
				d[e.to] = min(d[e.to] , d[e.from] + e.dist);
			}
		}
	}
	for (int i=0; i<edges.size(); i++){
		Edge  e = edges[i];
		if (d[e.to] > d[e.from] + e.dist) return true;
	}
	return false;
}

int main(){
	int T,m,w;
	scanf("%d",&T);
	while (T--){
		init();
		scanf("%d%d%d",&n,&m,&w);
		int x,y,z;
		for (int i=0; i<m; i++){
			scanf("%d%d%d",&x,&y,&z);
			addEdge(x-1,y-1,z);
			addEdge(y-1,x-1,z);
		}
		for (int i=0; i<w; i++){
			scanf("%d%d%d",&x,&y,&z);
			addEdge(x-1,y-1,-z);
		}
		if (Bellman_ford(0)) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

SPFA:

    用于快速求解单源最短路和判断图中是否有负权回路,是Bellma_ford的改进,采用了队列优化;期望的时间复杂度为O(E);

算法思想:(源点为s;  d[i]记录节点i 到源点的当前最短距离);

(1)初始化d[s] = (s == 0 ? 0 : INF),维护一个队列,里面存放所有需要进行迭代的点,初始化只有一个点s;并用一个数组表示节点是否在队列中,一个数组记录节点的入队次数。

(2)每次迭代时取队首元素u;枚举所有与之相连的边v;(u->v)设其权值为w;判断d[u] + w < d[v];如果成立:更新d[v];判断v是否在队列中,若不在,则入队并判断节点v入队次数,如果入队次数大于等于n则存在负权回路;

poj3259   SPFA模板题目(判断有无负权回路);

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<string>
#include<map>
#define ll long long
#define MAX 1000
#define INF 1000000000
#define eps 1e-8

using namespace std;

struct Edge{
	int from,to,dist;
};

vector<int>G[MAX];
vector<Edge>edges;

int n;

void init(){
	for (int i=0; i<=n; i++) G[i].clear();
	edges.clear();
}

void addEdge(int from, int to,int dist){
	edges.push_back((Edge){from,to,dist});
	int k = edges.size();
	G[from].push_back(k-1);
}

bool Bellman_ford(int s){
	bool inq[MAX];          //记录节点是否在队列中
	int cnt[MAX];           //记录节点入队的次数
	queue<int>q;
	int d[MAX];
	memset(inq,0,sizeof(inq));
	memset(cnt,0,sizeof(cnt));
	for (int i=0; i<n; i++) d[i] = (i == s ? 0 : INF);
	for (int i=0; i<n; i++){
		q.push(i);            //首先让所有点都入队,防止图不连通。 求连通图的最短路时,只需先将源点入队就行 
		inq[s] = true;
                //d[i] = 0;  只是判断有无负权回路可以直接让d[i] = 0;并让所有的节点入队
        }
	while (!q.empty()){
		int u = q.front();
		q.pop();
		inq[u] = false;
		for (int i = 0; i<G[u].size(); i++){
			Edge e = edges[G[u][i]];
			if (d[e.to] > d[u] + e.dist){
				d[e.to] = d[u] + e.dist;
				if (!inq[e.to]){        //思考?什么时候才需要入队 
					q.push(e.to);
					inq[e.to] = true;
					if (++cnt[e.to] >= n) return true;   //当某个节点入队超过n次说明图中存在负权回路 
				}
 			}
		}
	}
	return false;
}

int main(){
	int T,m,w;
	scanf("%d",&T);
	while (T--){
		init();
		scanf("%d%d%d",&n,&m,&w);
		int x,y,z;
		for (int i=0; i<m; i++){
			scanf("%d%d%d",&x,&y,&z);
			addEdge(x-1,y-1,z);
			addEdge(y-1,x-1,z);
		}
		for (int i=0; i<w; i++){
			scanf("%d%d%d",&x,&y,&z);
			addEdge(x-1,y-1,-z);
		}
		if (Bellman_ford(0)) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

         

    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/zyjhtutu/article/details/38366751
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞