针对dijkstra不能解决负权边而有的时间复杂度比较高的算法。
单源最短路径,时间复杂度为O(n^3)
要求:不能存在负权回路,但是可以反映出这一情况。
运用松弛技术,每个顶点v属于V,逐步减小,源S到v的最短路径的估计值d[v],直至其
达到最短路径的权(s,v),...
Bellman-ford(G,w,s)
INITIALIZE-SINGLE-SOURCE(G,s)
FOR i <-- to |V(G)-1|
do for each edge(u,v) 属于 E(G)
do relax (u,v,w)
for each edge(u,v) 属于 E[G]
do if d[v]>d[u]+w[u,v]
then return false
return true
与dijkstra的区别是bellman没用贪心,而是暴力不断迭代,所以其时间规模较dijkstra有点大。
递推:
dist(1)[u]=edge[v0][u]
dist(k)[u]=min{dist(k-1)[u],min{dist(k-1)[j]+edge[j][u]}}
可优化为:
dist(0)[v]=inf,dist(0)[v0]=0
dist(k)[v]=min{dist(k-1)[v],dist(k-1)[u]+w[u,v]}
dist(1)[u]表示从原点v0到u的只经过一条边的最短路径长度
dist(n-1)[u]表示从原点v0到u(不形成回路)经过n-1条边的最短路径长度
递推实现代码:
#include<iostream> #include <cstdio> #define inf 1000000 #define maxn 8 int n; int Edge[maxn][maxn]; int dist[maxn]; int pre[maxn]; using namespace std; bool bellman(int v0) { int i,j,k,u,temp; for(i=0;i<n;i++) { dist[i]=Edge[v0][i]; if(i!=v0&&dist[i]<inf) pre[i]=v0; else pre[i]=-1; } for(k=2;k<n;k++) { for(u=0;u<n;u++) { if(u!=v0) { for(j=0;j<n;j++) { if(Edge[j][u]<inf&&dist[j]+Edge[j][u]<dist[u]) { dist[u]=dist[j]+Edge[j][u]; pre[u]=j; } } } } } for(i=0;i<n;i++) { for(j=0;j<n;j++) { if(Edge[i][j]<inf&&dist[i]+Edge[i][j]<dist[j]) return false; } } return true; } int main() { bool flag; int i,j; int u,v,w; scanf("%d",&n); while(scanf("%d%d%d",&u,&v,&w)) { if(u==-1&&v==-1&&w==-1) break; Edge[u][v]=w; } for(i=0;i<n;i++) { for(j=0;j<n;j++) { if(i==j) Edge[i][j]=0; else if(Edge[i][j]==0) Edge[i][j]=inf; } } flag=bellman(0); if(flag) { int shortest[maxn]; for(i=1;i<n;i++) { printf("%d\t",dist[i]); memset(shortest,0,sizeof(shortest)); int k=0; shortest[k]=i; while(pre[shortest[k]]!=0) { k++;shortest[k]=pre[shortest[k-1]]; } k++;shortest[k]=0; for(j=k;j>0;j--) printf("%d->",shortest[j]); printf("%d\n",shortest[0]); } } else printf("error\n"); return 0; } /* 7 0 1 6 0 2 5 0 3 5 1 4 -1 2 1 -2 2 4 1 3 2 -2 3 5 -1 4 6 3 5 6 3 -1 -1 -1 */
bool bellman(int v0) { int i,j,k,u,temp,S[maxn]; for(i=0;i<n;i++) { S[i]=0; dist[i]=Edge[v0][i]; if(i!=v0&&dist[i]<inf) pre[i]=v0; else pre[i]=-1; } S[v0]=1; for(i=0;i<n;i++) { u=v0,temp=inf; for(j=0;j<n;j++) { if(!S[j]&&dist[j]<temp) u=j,temp=dist[j]; } S[u]=1; for(k=0;k<n;k++) { if(!S[k]&&Edge[u][k]&&dist[k]>dist[u]+Edge[u][k]) dist[k]=dist[u]+Edge[u][k],pre[k]=u; } } for(k=2;k<n;k++) { for(u=0;u<n;u++) { if(u!=v0) { for(j=0;j<n;j++) { if(Edge[j][u]<inf&&dist[j]+Edge[j][u]<dist[u]) { dist[u]=dist[j]+Edge[j][u]; pre[u]=j; } } } } } for(i=0;i<n;i++) { for(j=0;j<n;j++) { if(Edge[i][j]<inf&&dist[i]+Edge[i][j]<dist[j]) return false; } } return true; }
若采用邻接表做bellman-ford时间复杂度将下降为O(n*m)
bool bellman(int v0) { int i,k; for(i=0;i<n;i++) { dist[i]=inf; pre[i]=-1; } dist[v0]=0; for(k=2;k<n;k++) { for(i=0;i<n;i++) { if(dist[Edge[i].u]!=inf&&Edge[i].w+dist[Edge[i].u]<dist[Edge[i].v]) { dist[i].v=Edge[i].w+dist[Edge[i].u]; pre[Edge[i].v]=Edge[i].u; } } } for(i=0;i<m;i++) { if(dist[Edge[i].u]!=inf&&Edge[i].w+dist[Edge[i].u]<dist[Edge[i].v]) return false; } return true; }
bellman-ford还是用邻接表比较好,虽然存储是有些麻烦。
//
SPFA:
bellman-ford的队列实现,减少了不必要的冗余判断。时间复杂度为O(k*m),k为每个顶点入队的平均次数,对于通常的情况k==2.
初始时讲源点加入队列Q ,每次从队列中取出一个顶点,并对所有与他相邻的顶点进行松弛操作,若松弛成功则将其入队(改变过的u),重复操作,直至队列为空。
SPFA(G,w,s)
INITALIZE-SINGLE-SOURCE(G,s)
INITLALIZE-QUEUE(Q)
ENQUEUE(Q,s)
WHILE Q != NULL
do u <-- DLQUEUE(Q)
do tmp <-- d[v]
relax(u,v,w)
if(d[v]<tmp and v not in Q)
ENQUEUE(Q,v)
SPFA 算法和bfs有一定的相似性,不同的是bfs搜索中一个顶点出了队列就不可能再次被放入队列,但是spfa在一个顶点可能在出了队列之后再次被放入队列,也就是说一个顶点改进过其他顶点之后,过了一段时间后可能本身会被改进,于是再次用来改进其他顶点,这样反复迭代下去。
#include <cstdio> #include <iostream> #include <sstream> #include <cstring> #include <cmath> #include <string> #include <vector> #include <stack> #include <queue> #include <set> #include <map> #include <algorithm> #define inf 1000000 #define maxn 10 using namespace std; struct ArcNode{ int to; int weight; ArcNode *next; }; queue <int >Q;//队列中的节点为顶点序号。 int n; ArcNode *list[maxn];//每个顶点的边链表表头指针 int inq[maxn]; int dist[maxn]; int pre[maxn]; void SPFA(int v0) { int i,u; ArcNode *temp; for(i=0;i<n;i++) { dist[i]=inf;pre[i]=v0;inq[i]=0; } dist[v0]=0;pre[v0]=v0;inq[v0]++; Q.push(v0); while(!Q.empty ()) { u=Q.front (); Q.pop (); inq[u]--; temp=list[u]; while(temp!=NULL) { int v=temp->to; if(dist[v]>dist[u]+temp->weight) { dist[v]=dist[u]+temp->weight; pre[v]=u; if(!inq[v]) {Q.push (v);inq[v]++;} } temp=temp->next; } } } int main() { int i,j; int u,v,w; scanf("%d",&n); memset(list,0,sizeof(list)); ArcNode *temp; while(~scanf("%d%d%d",&u,&v,&w)) { if(u==-1&&v==-1&&w==-1) break; temp=new ArcNode; temp->to=v; temp->weight=w; temp->next=NULL; if(list[u]==NULL) list[u]=temp; else { temp->next=list[u]; list[u]=temp; } } SPFA(0); for(j=0;j<n;j++)//释放空间 { temp=list[j]; while(temp!=NULL) { list[j]=temp->next;delete temp;temp=list[j]; } } int shortest[maxn]; for(i=1;i<n;i++) { printf("%d\t",dist[i]); memset(shortest,0,sizeof(shortest)); int k=0; shortest[k]=i; while(pre[shortest[k]]!=0) { k++; shortest[k]=pre[shortest[k-1]]; } k++; shortest[k]=0; for(j=k;j>0;j--) printf("%d-->",shortest[j]); printf("%d\n",shortest[0]); } return 0; } /* 7 0 1 6 0 2 5 0 3 5 1 4 -1 2 1 -2 2 4 1 3 2 -2 3 5 -1 4 6 3 5 6 3 -1 -1 -1 */