Bellman-Ford(可解决负权边)--时间复杂度优化

Bellman-Ford 可解决带有负权边的最短路问题

  解决负权边和Dijkstra相比是一个优点,Bellman-Ford的核心代码只有4行::

u[],v[],w[] 分别存一条边的顶点、权值,dis[]存从 1 源点到各个顶点的距离

 

for(i=1;i<=n-1;i++)
    for(j=1;j<=m;j++)
        if(dis[v[j]] > dis[u[j]]+w[j])
            dis[v[j]] = dis[u[j]]+w[j];

 

 

  愿过程:

      循环n-1次,把每个顶点每条边都松弛;

     优化方法:

      ①,最坏的情况就是循环了n-1次才求出到每个顶点的最短路径,若果在n-1次之前就已经全部松弛完成,那么后面的循环就是多余

        优化:

for(k=1;k<=n-1;k++)//共有 n 个顶点,循环n-1次即可
{
        flag = 0;
        for(i=1;i<=m;i++)//对当前所有的边进行松弛
        {
            if(dis[v[i]] > dis[u[i]]+w[i])
            {
                dis[v[i]] = dis[u[i]]+w[i];
                flag = 1;
            }
        }
        if(flag == 0) break;//松弛也完成,结束
}

 

      ②,原过程:每次循环松弛过后,都有已经确定了的源点到某点最短的路径,此后这些顶点的最短路的值就会一直保持不变,不再受后续松弛操作的影响,但是每次还要判断是否需要松弛,这里浪费了时间。

     优化:确定一条边后总边数减一,把不能进行本次松弛的边再次后头存到原数组,松弛成功的边舍弃,再次松弛时只对未松弛的边进行操作,m的值会随着松弛预越来越小,直到全部完成。

for(k=1;k<=n-1;k++)//共有 n 个顶点,循环n-1次即可
    {
        m = M;//重新赋值后的 m 是数组中存储边的条数
        s = 1;flag = 0;
        for(i=1;i<=m;i++)//对当前所有的边进行松弛
        {
            if(dis[v[i]] > dis[u[i]]+w[i])
            {
                dis[v[i]] = dis[u[i]]+w[i];
                M--;    //松弛成功,边数减一
                flag = 1;
            }
            else//把本次不能进行松弛的边重新存储到当前的数组
            {
                u[s] = u[i];
                v[s] = v[i];
                w[s] = w[i];
                s++;
            }
        }
        if(flag == 0) break;//松弛也完成,结束
    }

附完整代码:

#include <stdio.h>
int main()
{
    int dis[10],i,k,m,n,s=1,u[10],v[10],w[10],M,flag;
    int inf = 99999999;
    scanf("%d%d",&n,&m);
    M = m;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u[i],&v[i],&w[i]);//输入各边及权值
    }
    for(i=1;i<=n;i++)
    {
        dis[i] = inf;//初始化为正无穷
    }
    dis[1] = 0;//以 1 为源点

    for(k=1;k<=n-1;k++)//共有 n 个顶点,循环n-1次即可
    {
        m = M;//重新赋值后的 m 是数组中存储边的条数
        s = 1;flag = 0;
        for(i=1;i<=m;i++)//对当前所有的边进行松弛
        {
            if(dis[v[i]] > dis[u[i]]+w[i])
            {
                dis[v[i]] = dis[u[i]]+w[i];
                M--;    //松弛成功,边数减一
                flag = 1;
            }
            else//把本次不能进行松弛的边重新存储到当前的数组
            {
                u[s] = u[i];
                v[s] = v[i];
                w[s] = w[i];
                s++;
            }
        }
        if(flag == 0) break;//松弛也完成,结束
    }

    for(i=1;i<=n;i++)
    {
        printf("%d ",dis[i]);
    }
    return 0;
}

 

 

 测试数据1:

5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3

 

运行结果:

0 -3 -1 2 4

 

测试数据2:

5 7
1 2 2
1 5 10
2 3 3
2 5 7
3 4 4
4 5 5
5 3 6

 

运行结果:

0 2 5 9 9

 

 

 

 

posted @ 2016-07-10 19:11  961897  阅读(2190)  评论(2编辑  收藏  举报