最短路,几种做法

输入:

本题目包含多组数据,请处理到文件结束。 
每组数据第一行包含两个正整数N和M(0<N<200,0<M<1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号。 
接下来是M行道路信息。每一行有三个整数A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路。 
再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点。
 输出: 对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1


用于解决最短路径问题的算法被称做“最短路径算法”, 有时被简称作“路径算法”最常用的路径算法有四种:

1.Dijkstra(迪杰斯特拉)------------解决单源最短路问题。(贪心的思想)----条件:非负权值。

2.Bellman-Ford(贝尔曼-福特)--------解决单源最短路问题。(逐个遍历每一条边)

3.Floyd(弗洛伊德)---------------解决全源最短路问题。(dp的思想)

4.SPFA(Shortest Path Faster Algorithm)-----------解决单元最短路问题。(队列实现,是bellman-Ford算法的一种改进)



方法一:bellmanford,    
Bellman-Ford算法可以大致分为三个部分
第一,初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的值设为无穷大(表示不可达)。
第二,进行循环,循环下标为从1到n-1(n等于图中点的个数)。在循环内部,遍历所有的边,进行松弛计算。
第三,遍历途中所有的边(edge(u,v)),判断是否存在这样情况:
d(v) > d (u) + w(u,v)
则返回false,表示途中存在从源点可达的权为负的回路。

Bellman-Ford算法可以解决图中有权为负数的边的单源最短路径问题。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 999999
int map[205][205];
int vis[205],dis[205];
int n,m;
int s,t;
void bellmanford()//美国数学家理查德•贝尔曼(Richard Bellman, 动态规划的提出者)和小莱斯特•福特(Lester Ford)发明
{
    int i,j,k;
    for(i=0; i<n; i++)
        dis[i]=map[s][i];//dis[]数组初始化,以s点为源点,向外扩散
    for(i=1; i<n; i++) //再循环n-1次
    {
        for(j=0; j<n; j++)
        {
            if(j!=s)//除去源点
            {
                for(k=0; k<n; k++)
                {
                    if(dis[k]>dis[j]+map[j][k])//此步的意思是从源点到各点的最小距离,路径经过所取过的点
                        dis[k]=dis[j]+map[j][k];
                }

            }
        }
    }
    if(dis[t]!=M)
        printf("%d\n",dis[t]);
    else
        printf("-1\n");
}
int main()
{
    int i,j;
    int a,b,c;
    while(~scanf("%d%d",&n,&m))
    {
        for(i=0; i<n; i++)
        {
            for(j=0; j<n; j++)
                map[i][j]=M;
            map[i][i]=0;
        }
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(map[a][b]>c)//这一步必须有,因为可能输入的点相同但距离不同,要取最小的那一个
                map[a][b]=map[b][a]=c;
            }
        scanf("%d%d",&s,&t);
        bellmanford();
    }
    return 0;
}

方法二:迪杰斯特拉算法 ------遍历经过的节点多,效率低---------要非负权值

1.从V-U中选择使dist[i]值最小的顶点i,将i加入到U中;

2.更新与i直接相邻顶点的dist值。(dist[j]=min{dist[j],dist[i]+matrix[i][j]})

3.知道U=V,停止。


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 999999
int map[205][205];
int vis[205],dis[205];
int n,m;
int s,t;
void Dijkstra()//迪杰斯特拉算法
{
    int i,j;
    int min,flag;
    for(i=0; i<n; i++)//此处从0还是1开始,要看点是从几开始编号的
        dis[i]=map[s][i];
    vis[s]=1;//源点
    for(i=1; i<n; i++)
    {
        min=M;
        for(j=0; j<n; j++)
        {
            if(!vis[j]&&dis[j]<min)//取最小的距离
            {
                min=dis[j];
                flag=j;
            }
        }
        vis[flag]=1;
        for(j=0; j<n; j++)
        {
            if(!vis[j]&&dis[j]>dis[flag]+map[flag][j])//重点之处
                dis[j]=dis[flag]+map[flag][j];
        }
    }
    if(dis[t]!=M)
        printf("%d\n",dis[t]);
    else
        printf("-1\n");

}
int main()
{
    int i,j,a,b,c;
    while(~scanf("%d%d",&n,&m))
    {
        memset(vis,0,sizeof(vis));//初始化归零,必须有
        for(i=0; i<n; i++)
        {
            for(j=0; j<n; j++)
                map[i][j]=M;
            map[i][i]=0;//此处必须有
        }
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(map[a][b]>c)//必须有,选取最小值
                map[a][b]=map[b][a]=c;//表明是无向图
        }
        scanf("%d%d",&s,&t);
        Dijkstra();
    }
    return 0;
}

方法三:Floyd(弗洛伊德)真不是很懂。

Floyd-Warshall算法的原理是动态规划

Di,j,k为从ij的只以(1..k)集合中的节点为中间节点的最短路径的长度。

  1. 若最短路径经过点k,则Di,j,k = Di,k,k − 1 + Dk,j,k − 1
  2. 若最短路径不经过点k,则Di,j,k = Di,j,k − 1

因此,Di,j,k = min(Di,k,k − 1 + Dk,j,k − 1,Di,j,k − 1)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 9999999
int map[205][205];
int n,m;
int s,t;
void Floyd()
{
    int i,j,k;
    for(k=0; k<n; k++)
        for(i=0; i<n; i++)
            for(j=0;j<n;j++)
                if(map[i][k]!=M&&map[k][j]!=M&&map[i][j]>map[i][k]+map[k][j])
                    map[i][j]=map[i][k]+map[k][j];
    if(map[s][t]!=M)
        printf("%d\n",map[s][t]);
    else
        printf("-1\n");
}
int main()
{
    int i,j;
    int a,b,c;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=0; i<n; i++)
        {
            for(j=0; j<n; j++)
                map[i][j]=M;
            map[i][i]=0;
        }
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(map[a][b]>c)
                map[a][b]=map[b][a]=c;
        }
        scanf("%d%d",&s,&t);
        Floyd();
    }
    return 0;
}

方法四:

就是在Bellman_Ford算法的基础上加上队列实现。

队列中的元素出队后还可以再进队。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 9999999
int map[205][205],q[205];
int dis[205],vis[205];
int n,m;
int s,t;
void SPFA()
{
    int i,p;
    int jin,chu;
    jin=chu=0;
    for(i=0; i<n; i++)
        dis[i]=M;
    dis[s]=0;
    q[jin++]=s;
    vis[s]=1;
    while(chu<jin)
    {
        p=q[chu++];
        for(i=0; i<n; i++)
        {
            if(dis[i]>dis[p]+map[p][i])
            {
                dis[i]=dis[p]+map[p][i];
                if(!vis[i])
                {
                    q[jin++]=i;
                    vis[i]=1;
                }
            }
        }
        vis[p]=0;
    }
    if(dis[t]!=M)
        printf("%d\n",dis[t]);
    else
        printf("-1\n");
}
int main()
{
    int i,j;
    int a,b,c;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(vis,0,sizeof(vis));
        for(i=0; i<n; i++)
        {
            for(j=0; j<n; j++)
                map[i][j]=M;
            map[i][i]=0;
        }
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(map[a][b]>c)
                map[a][b]=map[b][a]=c;
        }
        scanf("%d%d",&s,&t);
        SPFA();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值