谈谈对最短路径Floyd和Dijkstra算法基础部分的理解(一)(看不懂赔你钱)

       这个Dijkstra算法其实用矩阵来解释特别简单,邻接矩阵对此做了最好的诠释,我的习惯是从1开始看,如果1能到达其他结点的话就把那一条边的权值标在矩阵上,不能到达的话也就是用INF(无穷)来表示,因此也构造一个二维数组来存储他。

è¿éåå¾çæè¿°

这张图片看别人博客里面写的,感觉画的挺好的,就把他拿过来了。

1.我们要构造一个dis数组来存储第一号结点到其他结点的距离。

è¿éåå¾çæè¿°

既然是求1号顶点到其余顶点的最短的距离,那么我们就从dis数组中找到一个距离1号顶点的最短距离的顶点。通过dis数组可知,当前距离一号顶点的最短的顶点为2号顶点。所以1号顶点到2号顶点的最短距离为2。为什么呢?因为距离都为整数,所以不可能找到第三个中转点使得1号顶点到2号顶点的距离最短了。只要从1号顶点到中转点,那么他们间的距离已经大于1号到2号顶点的距离了!(值得我们注意的是:并不是说之后只要从dis数组中找到最短的距离,那么就是1号顶点到该点的最短距离了) 
当前我们找到了1号顶点到2号顶点的最短距离为2,那么我们来看看2号顶点有哪些出边呢?由邻接矩阵我们可以知道,2号顶点的出边为3号和4号顶点,我们看看2—->3号这条边能否比直接从1—->3的距离变短呢?也就是说我们来比较dis[3]和a[2][3]+dis[2]谁大谁小。可以知道a[2][3]+dis[2]小于dis[3]所以我们更新当前1号到3号的距离,为5,同理,我们知道1—>4号的距离也可以缩小到8,所以,dis的数组变为: 

2.è¿éåå¾çæè¿°

然后,我们继续找当前dis数组(除了2号顶点,因为我们已经找过了,所以在代码部分我们可以用一个数组来标记哪些点被找过了)距离1号顶点最短距离的顶点为5号顶点。然后我们看5号顶点有哪些出边,能否使得通过5号顶点使得某些点的距离变得更短。可以知道通过5号顶点可以使得1—>4号的距离缩短为5,1—->6号顶点的距离缩短为7。所以dis数组变为: 

3.è¿éåå¾çæè¿°

(5号顶点被用过了,我们就标记5号顶点) 
接下里我们从3,4,6号顶点找距离1号顶点最短的距离,当前为4号顶点,出边为4—>3,4—->6,没找到可以缩短的 

4.è¿éåå¾çæè¿°

继续从3,6找当前距离1号顶点最短的点为3,找3的出边,3—>5,dis[5]>a[3][5]+dis[3],没法缩短。 

5.è¿éåå¾çæè¿°

最后找到只剩下6这个顶点,没有出边。所以不dis不变 

6.è¿éåå¾çæè¿°

所以由dis数组可知,1号到1~6号的最短距离分别为,0,2,5,5,3,7

下面具体写一下代码来解释这个,其实要掌握的就是要写代码的能力,这个思路的话是比较好理解的,但是转换为计算机模型的话,刚开始可能会显得比较生硬。

//这个代码求的是第一个顶点到其他顶点的最短距离
#include<stdio.h>
int main()
{            //采用邻接矩阵来存储图
    int a[100][100],point,i,j,k,number,t1,t2,t3,u,v,min,book[10]={0},dis[10];
    //point表示顶点数,number表示边数,t1,t2,t3,表示t1到t2的距离为t3,\
	//book数组用来标记哪些点目前是最短的距离
	//dis数组用来存储第一个顶点到其余顶点的最短距离
    int inf=0x3f3f3f; //假设这个数表示无穷大
       scanf("%d %d\n",&point,&number);//输入这个图形有多少个点和多少个边 
       for(i=1;i<=point;i++)  //本人习惯数组从1开始,这样可以用a[1]来表示第一个顶点
           for(j=1;j<=point;j++){//其实这个循环开始就是把邻接矩阵的主对角线全变为0,然后其他都标记为INF,因为数据现在也没有输入进来。 
               if(i==j){
                   a[i][j]=0;//初始化,当每个顶点到自身时,距离为0,否则为无穷大
               }else{
                a[i][j]=inf;
               }
           }
        	//开始读入边,开始把第一次的邻接矩阵的1可直达的点的距离填上去,改掉部分的INF 
           for(i=1;i<=number;i++){
             scanf("%d %d %d",&t1,&t2,&t3);//这里只要针对边就可以了, 把有的边写到矩阵里面就可以了。 
             a[t1][t2]=t3;
           }
		   //存放1号顶点到其余顶点的距离,不可达则为inf(无穷大),dis数组的构建	
           for(i=1;i<=point;i++){
               dis[i]=a[1][i];
           }

    book[1]=1;//标记第一个顶点已经是最短距离
		   	  //book数组用来标记哪些点目前是最短的距离	
    //Dijkstra算法开始
    for(i=1;i<=point-1;i++){//这个循环是次数遍历 

          min=inf;
          for(j=1;j<=point;j++){    //求出当前dis数组中离第一个顶点最短的距离的顶点的下标
              if(book[j]==0 && dis[j]<min){
                    min=dis[j];
                    u=j;//记下这个点的下标
              }
          } 

          book[u]=1;//用book数组标记了哪些点被标记了,下一次循环的时候就不会遍历到这个点了, 

          for(k=1;k<=point;k++){  //不断地与前一个dis数组进行比较 
              if(a[u][k]<inf){
                   if(dis[k]>dis[u]+a[u][k]){
                          dis[k]=dis[u]+a[u][k];
              }
            }
          }     //若找到其他途径比从1号顶点直接到目的顶点的距离短,则替换掉
    }
    for(i=1;i<=point;i++){
        printf("1号顶点到%d号顶点的最短距离为------>%d\n",i,dis[i]);
    }
return 0;
}

运行结果就是:1号到1~6号的最短距离分别为,0,2,5,5,3,7

下面写一下我写过的用Floyd算法的题目(其实我觉得这两种算法真的差不多,Floyd算法的应用面更加广一点,像我下面一道题就是用了求任意两点间的最短距离问题)

                                             ZCMU1398工程

Description

 

某省自从实行了很多年的畅通工程计划后,终于修建了很多路。不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多。这让行人很困扰。

现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。

 

Input

 

本题目包含多组数据,请处理到文件结束。
每组数据第一行包含两个正整数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),分别代表起点和终点。

 

Output

对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1.

 

Sample Input

3 3 0 1 1 0 2 3 1 2 1 0 2 3 1 0 1 1 1 2

Sample Output

2 -1

-------------------------------------------------------------------------------------------------------------------------------------------------------

2018年7月28日补充:

这道题目的思路在于要从Floyd算法出发建立一个任意两个顶点最小距离的模型,也就是说如果我想从1点到3点的距离为3,而从1点到2点,再从2点到3点的距离和是2的话,我为什么不选择从1到2再到3呢,这个其实也就是这个算法的核心思想。

接下来就比较简单了,把这个算法写成函数,以后用到的话直接当成模板好了,然后主函数里面就是先从矩阵的思想出发就可以了,这个是一个邻接矩阵,特点是主对角线全为零,先把其他地方全部用INF表示,之后如果有数据了以后就直接替代就可以了,再输入路的具体情况就可以了。最后引用一下这个算法函数,这道题就AC了。

#include<cstdio>
#include<cstring>
#define INF 0x3f3f3f
int dis[220][220],n,m;//dis数组就是来表示邻接矩阵的,n表示城镇的数量,m表示路的条数
//狄杰斯特拉算法的开始,建立模型 
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 (dis[i][j]>dis[i][k]+dis[k][j])//算法的核心所在 
				dis[i][j]=dis[i][k]+dis[k][j];
			}
		}
	}
}
int main()
{
	while (scanf ("%d%d",&n,&m)!=EOF)//输入城镇的数量和路的条数 
	{
		int i,j;
		for (i=0;i<n;i++)//先初步把矩阵能够建立起来 
		{
			for (j=0;j<n;j++)
			{
				if (i==j)
				dis[i][j]=0;//将矩阵的主对角线全部标记为0 
				else
				dis[i][j]=INF;//然后其余的地方全部用无穷的INF表示 
			}
		}
		int a,b,c;
		for (i=0;i<m;i++)//先把路的通向情况全部放在矩阵里面,进行建立数据 
		{
			scanf ("%d%d%d",&a,&b,&c);
			dis[a][b]=dis[b][a]=c;//邻接矩阵的对称性的应用 
		}
		Floyd();//引用函数 
		scanf ("%d%d",&a,&b);
		if (dis[a][b]!=INF)//如果可达的话 
		printf ("%d\n",dis[a][b]);
		else//如果不可达的话 
		printf ("-1\n");
	}
	return 0;
}

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值