边上权值为任意值的单源最短路径问题——Bellman--Ford算法

Dijkstra算法的局限性:不能处理权值为负值的情形,Dijkstra算法在利用顶点u的dist[ ]值递推各顶点的dist[ ]值时,前提是顶点u的dist[ ]值是当前最短路径长度最小的,如果图中所有边的权值都是正的,这样推导没有问题,但如果有负权值的边,这样推导就不正确。

 

 

Bellman-Ford算法思想:dist[]

算法说明:

dist(1)[u]:从源点v0到终点u只经过一条边的最短路径长度。

dist(2)[u]:从源点v0出发最多经过不构成负权值回路的两条边到达终点u的最短路径长度

dist(3)[u]:从源点v0出发最多经过不构成负权值回路的三条边到达终点u的最短路劲长度

...............

dist(n-1)[u]:从源点v0出发最多经过不构成负权值回路的(n-1)条边到达终点u的最短路径长度

好抽象:举个栗子吧

假设我们需要最多经过三条边到达终点4,那么我们需要考虑经过三条边以后是否相对于上一次最多经过两条边到达4距离是否缩短了呢?

所有可能最多经过三条边到达终点4的情况:dist[1]+Edge[1][4], dist[2]+Edge[2][4], dist[3]+Edge[3][4],.....dist[n-1]+dist[n-1][4](此时dist[u]为最多经过两条边到达终点u的长度,加上一条直接变Edge[j][u]为三条边的情况),这些情况与最多经过两条边时进行比较,看看距离是否更短,如果更短,则更新dist[j]的值,否则,不变。

限制条件:要求图中不能包含权值总和为负值的回路。

例题:求顶点0到其他各顶点的最短路径长度,并输出对应的最短路径

输入

顶点数n 

u v w(每条边的端点和权值)

......

-1 -1 -1 输入结束

输出:

最短路径长度   最短路径

.............

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define INF 999999
#define MAXN 8
int n;
int Edge[MAXN][MAXN];
int dist[MAXN];
int path[MAXN];
void Bellman(int v0)///求源点v0到其他顶点的最短路径
{
    int i,j,k,u;
    for(i=0; i<n; i++)///初始化dist和path数组
    {
        dist[i]=Edge[v0][i];
        if(i!=v0&&dist[i]<INF)
            path[i]=v0;
        else
            path[i]=-1;
    }
    for(k=2; k<n; k++)///最多经过k条边时的情况
    {
        for(u=0; u<n; u++)///k改变时,每个终点u都可能改变
        {
            if(u!=v0)
            {
                for(j=0; j<n; j++)///列出经过k条边到终点u的所有可能情况,与经过k-1条边的时候进行比较
                {
                    if(Edge[j][u]<INF&&dist[j]+Edge[j][u]<dist[u])
                    {
                        dist[u]=dist[j]+Edge[j][u];
                        path[u]=j;
                    }
                }
            }
        }
    }
}
int main()
{
    int i,j;
    int u,v,w;
    scanf("%d",&n);
    while(1)
    {
        scanf("%d%d%d",&u,&v,&w);///因为需要确定在Edge中的起始终止边,所以需要u,v来暂存
        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;
        }
    }
    Bellman(0);
    int shortest[MAXN];
    for(i=1; i<n; i++)
    {
        printf("%d\t",dist[i]);
        memset(shortest,0,sizeof(shortest));
        int k=0;
        /*
        从0到终点i的路径:shortest[0]存终点i,通过终点i(也就是shortest[0]),找到倒数第二个顶点,
        通过倒数第二个顶点找到倒数第三个顶点,直到源点0
        */
        shortest[k]=i;
        while(path[shortest[k] ]!=0)
        {
            k++;
            shortest[k]=path[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
输出:
1       0->3->2->1
3       0->3->2
5       0->3
0       0->3->2->1->4
4       0->3->5
3       0->3->2->1->4->6

*/

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值