最短路之bellman-ford

来源:互联网 发布:支付宝付款需要网络吗 编辑:程序博客网 时间:2024/05/22 09:01

针对dijkstra不能解决负权边而有的时间复杂度比较高的算法。

单源最短路径,时间复杂度为On^3

要求:不能存在负权回路,但是可以反映出这一情况。

运用松弛技术,每个顶点v属于V,逐步减小,源Sv的最短路径的估计值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 8int 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;}/*70 1 60 2 50 3 51 4 -12 1 -22 4 13 2 -23 5 -14 6 35 6 3-1 -1 -1*/

在此基础上若用dijkstra求出上界,则会加速bellman-ford的迭代过程:
//即先用dijkstra求一遍。因为存在负权边,所以求出来的不一定是最短边,但其上界就大大缩小了。再用bellman-ford求解速度加快。但这样做也并没有改变其时间复杂度。
改过的代码:
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 10using  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;}/*70 1 60 2 50 3 51 4 -12 1 -22 4 13 2 -23 5 -14 6 35 6 3-1 -1 -1*/


原创粉丝点击