最短路

    迪杰斯特拉算法   

    迪杰斯特拉算法,说出来你们可能不信,去年开始接触,直到写这篇博文时才初步明白,唉,惭愧惭愧,我这脑子也没谁了,就一道HDU 2066还是在我看了一位仁兄的解题思路后才明白的,其实只有代码,,,,

        但是我觉得却增加了我对该算法的理解,现在就借这位仁兄的所写谈谈我对迪杰斯特拉算法的理解吧。现附上这位仁兄的网址点击打开链接,

同时感谢师哥们的讲解,哈哈哈。

      迪杰斯特拉算法主要是用于解决“单源最短路径”问题,首先来介绍什么是单源最短路径,百度可知,给定一个带权有向图G=(V,E),其中每条边的权值是一个实数,同时给定V中的一个顶点,称之为源,通常题目中会隐晦的默认源点,例如从某地出发等,现计算从源到各个顶点的最短路径,将此类问题称之为最短路问题。其中,迪杰斯特拉算法主要用于解决求权值为非负实数的单源最短路问题。

     一 . 方法和步骤大体为:

1.求出源点到图中其他各个点的直接距离,直接距离指的是两地之间联通不经过第三方地点,所以题目中一般会直接给出,其他不与源点直接相连的点即无法求直接距离的点将其距离定义为无穷大。

2.依次找到源点到图中其他各个顶点的距离的最小值及其对应的点,确认这个距离是源点到该点的最短距离,。

3.在确定源点到一个新的顶点的最短距离后,更新该源点到其他没有确定最短距离的点的距离。

4.重复2,3步,直到所有点确定最短距离。

    简单地说就是求各个点的最短路,将其连接即为最短路求解,这也是最短路的一个定义之一。

        二 . 迪杰 斯特拉算法算法的实现:

       首先是定义一个二维数组dis[N][N],用来诠释方法步骤的第一步,同时存一个leastcost[N]数组,储存源点到其他各个顶点的距离,可以直接调用,在初始时可以将leastcost[N]诠释方法第一步,在初始时和二维数组方法一样,都是贮存源点与其他各个顶点关系。

下面是我自己写的一份对迪杰斯特拉的详解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#define MAX 1000
#define inf 1<<20
using namespace std;
int vis[MAX];//检测节点是否已经加入最短路
int map[MAX][MAX];//邻接矩阵存储各对应点的关系
int lowcost[MAX];//此处lowcost数组存储初始顶点(出发顶点)到其他各点的最短路径
int N,num;
void init()//初始化
{
    for(int i=0; i<=N; i++)
    {
        for(int j=0; j<=N; j++)
        {
            map[i][j]=inf;
        }
    }
}
void DIS(int v)
{
    for(int i=1; i<=N; i++)
    {
        lowcost[i]=map[v][i];

        vis[i]=0;
    }
    vis[v]=1;
    int k;
    int min;
    for(int i=1; i<=N; i++)
    {
        min=inf;
        for(int j=1; j<=N; j++)
        {
            if(!vis[j]&&lowcost[j]<min)
            {
                min=lowcost[j];

                k=j;
            }
        }//多次循环,找出路径最小点,再从此处出发
        if(vis[k]==1)break;
        vis[k]=1;
        for(int j=1; j<=N; j++)
        {
            if(!vis[j]&&min+map[k][j]<lowcost[j])
            {
                lowcost[j]=min+map[k][j];
            }
        }//松弛操作
    }
    printf("%d\n",lowcost[N]);
}
int main()
{

    int u,v,cost;
    while(scanf("%d%d",&N,&num)&&(N+num))
    {
        init();
        for(int i=1; i<=num; i++)
        {
            scanf("%d%d%d",&u,&v,&cost);
            map[u][v]=cost;
            map[v][u]=cost;
        }
        DIS(1);//从一开始走
    }
    return 0;
}

运行结果如下


       然后 ,,,额,,,,口才不好,以师哥推荐的一道杭电HDU 2066题为例,贴一下代码片,在代码片中解释一下

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define zz 1<<20//定义无穷远的距离大小,代表2的20次方
using namespace std;
int dis[1000+5][1000+5],vis[1005],leastcost[1005];
//hh数组代表各店对应位置,dis代表起始点到各个点的最小值
int Dijkstra(int n)
{
    int minid;
    memset(vis,0,sizeof(vis));//vis数组清零
    for(int i=1;i<=n;i++)
    {
        leastcost[i]=dis[0][i];
    }//用dis数组代表源点到各个定点的距离
    vis[0]=1;//0节点为第一个被访问的节点
    while(1)
    {
       int inf=zz;
       for(int j=1;j<=n;j++)
       {
           if(!vis[j]&&leastcost[j]<inf)
           {
               inf=leastcost[j];//不断更新inf值,直至找到最小的,同时要求
               minid=j;         //满足在规定数组范围内,将minid更新为对应的位置值
           }
       }
       if(inf==zz)//证明不存在可求直接距离的点,则无法求解
       {
           break;
       }
       vis[minid]=1;
       for(int j=1;j<=n;j++)//dis[minind][j]其中的minid代表在此位置为最小直接路径
       {
           if(!vis[j]&&dis[minid][j]+leastcost[minid]<leastcost[j])
           {
               leastcost[j]=dis[minid][j]+leastcost[minid];
           }//对路径较大的节点路程进行松弛
       }
    }
    return leastcost[n];
}
int main()
{
    int T,S,D;
    int a,b,c;
    int tmp;
    while(scanf("%d%d%d",&T,&S,&D)!=EOF)
    {
        int n=-1;
        for(int i=0;i<1005;i++)
        {
            for(int j=i;j<1005;j++)
            {
                dis[i][j]=dis[j][i]=zz;
            }
        }//先将所有路径都化为无穷远,之后在下面填充
        while(T--)
        {
            scanf("%d%d%d",&a,&b,&c);
            n=max(max(a,b),n);
            if(dis[a][b]>c)
            {
                dis[a][b]=dis[b][a]=c;
            }//此处因为先前值为无穷大,所以可以对应值更新为各个直接距离
        }
        while(S--)
        {
            scanf("%d",&tmp);
            n=max(tmp,n);//n值取最大为的是尽量扩大算法中二维数组范围
            dis[0][tmp]=dis[tmp][0]=0;//tmp和源点直接距离为0
        }
        n++;//此二维数组由零开始,所以n++
        while(D--)
        {
            scanf("%d",&tmp);
            dis[n][tmp]=dis[tmp][n]=0;
        }
        cout<<Dijkstra(n)<<endl;
    }
    return 0;
}

关于此题解还有一些理解不足之处,日后我会再完善,还有有关迪杰斯特拉算法算法优化以及其他最短路问题求解算法以后我还会做总结。希望大神们帮我指点指点。
再加一道基础题poj2387 https://vjudge.net/contest/66569#problem/A

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
using namespace std;
typedef long long LL;
#define inf 1<<29
int visit[1000+5];
int tu[1000+5][1000+5];
int shor[1000+5];
int T,N;
void init()
{
    for(int i=1; i<=N; i++)
    {
        for(int j=1; j<=N; j++)
        {
            tu[i][j]=inf;
        }
        visit[i]=0;
    }
}
int zz;
int k;
int dis(int v)
{
    for(int i=1;i<=N;i++)
    {
        shor[i]=tu[1][i];
    }
    visit[v]=1;
    for(int i=1; i<=N; i++)
    {
        zz=inf;
        for(int j=1; j<=N; j++)
        {
            if(!visit[j]&&shor[j]<zz)
            {
                zz=shor[j];
                k=j;
            }
        }//筛选最小值
        if(visit[k]==1)break;
        visit[k]=1;
        //cout<<"k    "<<k<<endl;
        for(int j=1;j<=N;j++)
        {
            if(!visit[j]&&zz+tu[k][j]<shor[j])
            {
                shor[j]=zz+tu[k][j];
                //cout<<"hh    "<<shor[j]<<endl;
            }
        }
        //cout<<visit[k]<<endl;
    }
    return shor[N];
}
int main()
{
    int a,b,len;
    while(scanf("%d%d",&T,&N)!=EOF)
    {
        init();
        for(int i=0; i<T; i++)
        {
            scanf("%d%d%d",&a,&b,&len);
            if(tu[a][b]>len)
            {
                tu[b][a]=tu[a][b]=len;
            }

        }
        printf("%d\n",dis(1));
    }
    return 0;
}

堆优化

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<vector>
#include<utility>//pair头文件
using namespace std;
#define INF 1<<23
typedef long long LL;
const int MAX_N=10000;

struct edge
{
    int to;//终点
    int cost;//路长
};
typedef pair<int,int> P;//first最短距离,second为顶点的编号
int t,n;
int a,b,len;
vector<edge>G[MAX_N];
int shor[MAX_N];

void dis(int v)
{
    n++;
    priority_queue<P,vector<P>,greater<P> >q;//将数据按照first从小到大的顺序排序
    fill(shor,shor+n,INF);//对数组shor进行赋值
    n--;
    shor[v]=0;
    q.push(P(0,v));//v到v即自身的最短距离为0;
    while(!q.empty())
    {
        P p=q.top();
        q.pop();
        int k=p.second;//k为当前顶点
        if(shor[k]<p.first)continue;
        //当前顶点所存储的边长大于shor数组当中记录的最小值时
        for(int i=0; i<G[k].size(); i++)
        {
            edge e=G[k][i];
            //松弛操作
            if(shor[e.to]>shor[k]+e.cost)
            {
                shor[e.to]=shor[k]+e.cost;
                q.push(P(shor[e.to],e.to));
            }
        }
    }
}
int tu[MAX_N][MAX_N];
void init()
{
    for(int i=0; i<=n; i++)
    {
        for(int j=0; j<=n; j++)
        {
            tu[i][j]=INF;
        }
    }
}
int main()
{
    while(scanf("%d%d",&t,&n)!=EOF)
    {
        init();
        while(t--)
        {
            scanf("%d%d%d",&a,&b,&len);
            edge e,k;
            e.cost=len;
            e.to=b;
            G[a].push_back(e);
            k.to=a;
            k.cost=len;
            G[b].push_back(k);
        }
        dis(1);
        for(int i=1;i<=n;i++)
        {
            cout<<shor[i]<<endl;
        }
        cout<<endl;
        printf("%d\n",shor[n]);
    }
    return 0;
}

弗洛伊德算法

可以求出任意两点的最短路

结果为map[i][j](i<j)

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
int main()
{
    int n,m,a,b,c;
    int map1[100][100];
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            if(i==j) map1[i][j]=0;
            else map1[i][j]=inf;
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        map1[a][b]=c;
    }
    for(int k=1; k<=n; k++)
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
            {
                if(map1[i][j]>map1[i][k]+map1[k][j])
                    map1[i][j]=map1[i][k]+map1[k][j];
            }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
            printf("%d ",map1[i][j]);
        printf("\n");
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值