Bellman_ford算法,SPFA

来源:互联网 发布:如何评价张作霖 知乎 编辑:程序博客网 时间:2024/06/05 01:51

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-8using 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-8using 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;}



         

0 0
原创粉丝点击