最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)

以最简单的   Til the Cows Come Home    为例 点击打开链接

几种算法的核心思想就是,先找到距离起点最近的点,以它为松弛点,对所有的点进行松弛操作;

1.dijkstra-邻接矩阵(无法处理负权值)

       以二维数组为存储方式,以节点的序号为数组的横纵坐标来存储权值;

 以图为例:

       《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》

           存储方式:

《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》              《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》

             到自身的位置为0,有权值的赋为权值,没有的赋为无穷大(0x3f3f3f3f)

            设一数组dis[],存储起点到各点的最小值;

            先初始化:           

         《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》

           然后以距离起点最近的点为松弛点,对每个点进行松弛;

          详细解释请看代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define MAX 5000
#define inf 0x3f3f3f3f//较大的数,可以看为无穷大

int n,t,i,j;
int map[MAX][MAX],dis[MAX],xia[MAX];//map记录题目给出的各条边的权值,dis记录起点到到每个点的最短路,xia记录那些点进行过松弛操作

int main()
{
    while(~scanf("%d%d",&t,&n))
    {
        for(i=1; i<=n; i++)//对map进行初始化操作
            for(j=1; j<=n; j++)
                if(i==j) map[i][j]=0;//到自己的距离为零
                else map[i][j]=inf;
        for(i=1; i<=t; i++)//记录题目给的每一条边
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);//可能有重复的边所以要记录最短的距离
            map[a][b]=map[b][a]=min(map[a][b],c);//因为是无向图,所以可以双向走:如果是有向图就改为:map[a][b]=min(map[a][b],c);
        }
        memset(xia,0,sizeof(xia));//记录那些点进行过松弛操作
        xia[1]=0;
        for(i=1; i<=n; i++)//对dis初始化
            dis[i]=map[1][i];//如果不是求1到n的最短距离,就改成:dis[i]=map[起点的标号][i];
        int h,x;
        for(i=1; i<=n; i++)//重点。。松弛操作
        {
            h=inf;
            for(j=1;j<=n;j++)//求出距离起点最近的点
            {
                if(xia[j]==0&&h>dis[j])
                h=dis[x=j];//x记录距离起点最近的点
            }
            xia[x]=1;//并且记录对x进行过松弛操作
            for(j=1;j<=n;j++)//对每个点进行松弛操作,求出最短距离
                dis[j]=min(dis[j],dis[x]+map[x][j]);
        }
        printf("%d\n",dis[n]);//输出起点到n的最短距离;
    }
    return 0;
}

  dijkstra-邻接表(不能处理负权值)

  邻接表比邻接矩阵快的多;(难理解,不好解释)这是啊哈算法上的解释

《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》

《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》

《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》

《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》《最短路问题(4种方法)(邻接矩阵,邻接表,bellman-ford,spfa)》

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define MAX 5000
#define inf 0x3f3f3f3f

int dis[MAX],u[MAX],v[MAX],w[MAX],first[MAX],next[MAX];
bool xia[MAX];
int i,j,n,t;

int main()
{
    while(~scanf("%d%d",&t,&n))
    {
        memset(xia,false,sizeof(xia));//初始化操作
        xia[1]=true;
        memset(first,-1,sizeof(first));//存储给定边的序号
        memset(next,-1,sizeof(next));//存储给定边的序号
        memset(dis,inf,sizeof(dis));//记录1到各个点的最短路
        dis[1]=0;
        for(i=1;i<=2*t;i+=2)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);//下面是记录无向图的,代码比有向图多一倍,核心操作
            u[i]=v[i+1]=a,u[i+1]=v[i]=b,w[i]=w[i+1]=c;
            next[i]=first[u[i]],next[i+1]=first[u[i+1]];
            first[u[i]]=i,first[u[i+1]]=i+1;
        }
        int f=1,hmax;
        for(i=2;i<=n;i++)//核心操作
        {
            for(j=first[f];j!=-1;j=next[j])//找出从f点出发的边的终点的最短距离
            {
                if(xia[v[j]]==false)//找最短距离
                    dis[v[j]]=min(dis[v[j]],dis[f]+w[j]);
            }
            hmax=inf;
            for(j=1;j<=n;j++)
            {
                if(xia[j]==false&&hmax>dis[j])
                    hmax=dis[f=j];//f记录到1最近的点
            }
            xia[f]=true;//对松驰过的点进行标记
        }
        printf("%d\n",dis[n]);
    }
    return 0;
}

2.bellman-ford(可以处理负权值的问题)(目前唯一一个)

核心代码只有四行

n代表点的数量,要进行n-1次松弛操作,t代表边的数量

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

想法与上面相同;

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define MAX 5000
#define inf 0x3f3f3f3f


int u[MAX],v[MAX],w[MAX],dis[MAX];//u记录起始点,v记录终止点,w记录这条边的权值,dis记录1到各个点的最短距离
int i,j,n,t;


int main()
{
    while(~scanf("%d%d",&t,&n))
    {
        memset(u,0,sizeof(u));//初始化
        memset(v,0,sizeof(v));
        memset(w,0,sizeof(w));
        memset(dis,inf,sizeof(dis));
        dis[1]=0;
        for(i=1; i<=t; i++)//输入数据
            scanf("%d%d%d",&u[i],&v[i],&w[i]);
        for(i=1; i<=n-1; i++)//n为点的数量
            for(j=1; j<=t; j++)//t为边的数量
            {
                dis[v[j]]=min(dis[v[j]],w[j]+dis[u[j]]);//求最小值,如果是有向图,就没有下面那一行代码
                dis[u[j]]=min(dis[u[j]],w[j]+dis[v[j]]);
            }
        printf("%d\n",dis[n]);
    }
    return 0;
}

3.floyd(不太常用)

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define MAX 5000
#define inf 0x3f3f3f3f

int map[MAX][MAX];
int i,j,k,n,t;

int main()
{
    while(~scanf("%d%d",&t,&n))
    {
        for(i=1; i<=n; i++)
            for(j=1; j<=n; j++)
                if(i==j) map[i][j]=0;
                else map[i][j]=inf;
        for(i=1; i<=t; i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            map[a][b]=min(map[a][b],c);
        }
        for(k=1; k<=n; k++) //
            for(i=1; i<=n; i++)
                for(j=1; j<=n; j++)
                    if(map[i][j]>map[i][k]+map[k][j])
                        map[i][j]=map[i][k]+map[k][j];
        printf("%d\n",map[1][n]);
    }
    return 0;
}

4.spfa

spfa 是bellman-ford的优化,用队列来优化。

将能通过这个点优化其他点的点放入队列中,再用队列中的点来松弛与这个点直接相连的点,直到队列中没有点为止。

来个例题好理解。 题目链接:传送门

有n个点,m条边,u,v边的两个点,w边的长度,求1到n的最短路;

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#define inf 0x3f3f3f3f
#define maxx 5009
using namespace std;
struct ndoe
{
    int u,v,w;
    int next;
}dis[maxx];
int first[maxx];
int dp[maxx];//记录最短路
int xia[maxx];//该点是否在队列中
int M;
void set_dis(int u,int v,int w)//建立邻接表
{
    dis[M].u=u;
    dis[M].v=v;
    dis[M].w=w;
    dis[M].next=first[u];
    first[u]=M++;
}
int main()
{
    int m,n;
    while(~scanf("%d%d",&m,&n))
    {
        M=0;
        memset(first,-1,sizeof(first));
        memset(xia,0,sizeof(xia));
        memset(dp,inf,sizeof(dp));
        for(int i=1; i<=m; i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            set_dis(a,b,c);
            set_dis(b,a,c);
        }
        dp[1]=0;//初始化
        xia[1]=1;
        queue<int>q;
        q.push(1);//先将起始点放入队列中
        int x;
        while(!q.empty())
        {
            x=q.front();
            q.pop();
            xia[x]=0;
            for(int j=first[x]; j!=-1; j=dis[j].next)
            {
                if(dp[dis[j].v]>dp[x]+dis[j].w)
                {
                    dp[dis[j].v]=dp[x]+dis[j].w;
                    if(xia[dis[j].v]==0)//这个点被松弛过,所以也可以松弛别的点,放入队列中
                    {
                        q.push(dis[j].v);
                        xia[dis[j].v]=1;
                    }
                }
            }
        }
        printf("%d\n",dp[n]);
    }
    return 0;
}

    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/nhl19961226/article/details/54580847
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞