图论之最短路径(2)——Bellman-Ford算法

继续最短路径!说说Bellman—Ford算法

思路:假设起点为s,图中有n个顶点和m个边,那么它到任一点(比如i)的最短路径

最多可以有n-1条(没有回路就是n-1条);因为最短路径中不可能包含回路:如果有正权

回路(正圈),那么最短路径肯定不走这个回路(不绕圈,绕圈会增加权值,直接走),

如果有负权回路(负圈),那么就不存在最短路径,因为每走一次负圈权值就减少一次,

根本不存在最小值。
我们再次利用松弛的办法:每一轮,我们枚举所有的边,看不能不能缩短两个顶点之间的

距离。到i顶点的缩短就意味着可以经过一个另一个的顶点来缩短起点到i顶点的距离,换

句话说,最短距离多了一条边。每进行一轮松弛,最短距离就可能多一条边,而最短路径

最多有n-1条边,所以进行n-1轮次松弛就可以了。
核心代码如下:
for(int k=1;k<=n-1;k++)
 for(int i=1;i<=m;i++)
  if(dis[v[i]]>dis[u[i]]+w[i])
   dis[v[i]] = dis[u[i]] + w[i];
现在分析:首先要注意的是使用该算法时,我们没有使用邻接矩阵或者邻接表。而是采用

三个数组u,v,w来记录每一条边(因为我们只利用边)。三个对应的值:u[i],v[i],w[i]

表示从u[i]到v[i]有一条权值为w[i]边。
接下来每一轮我们都枚举每一条边,来尝试能否缩短起点s到该边的终点v[i]的距离。注

意,如果s不能到达u[i],那么dis[u[i]]就是INF,这个松弛就是失败的(理解一下)。
这样的算法是有优化的余地的,我们可以看到每一轮松弛中是有失败的,也就浪费了时间

。之后还可以优化。

另外还可以用Bellman-Ford算法来计算图中是否有负圈。如果我们增加轮数为n,那么如

果第n轮有dis[]有更新就说明是有负圈的。

代码如下

# include<iostream>

using namespace std;

int dis[1000];//保存临时值(或者确定值)
int u[1000], v[1000], w[1000];

const int INF = 99999999;

int main()
{
    int n, m;//n个顶点,m条边。

    cin >> n >> m;

    for (int i = 0; i < m; i++)//读入m条边
        cin >> u[i] >> v[i] >> w[i];

    //初始化dis数组
    for (int i = 0; i < n; i++)
        dis[i] = INF;

    dis[0] = 0;//还是默认顶点0为起点s


    //Bellman-Ford算法核心
    for (int i = 0; i < n - 1; i++)//进行n轮松弛
        for (int k = 0; k < m; k++)//枚举m条边
            if (dis[v[k]] > dis[u[k]] + w[k])
                dis[v[k]] = dis[u[k]] + w[k];

    for (int i = 0; i < n; i++)
        cout << dis[i] << " ";

    system("pause");

    return 0;
}

 

你可能感兴趣的:(图论之最短路径(2)——Bellman-Ford算法)