数据结构基础:P8.3-图(三)--->图之习题选讲-旅游规划

本系列文章为浙江大学陈越、何钦铭数据结构学习笔记,前面的系列文章链接如下
数据结构基础:P1-基本概念
数据结构基础:P2.1-线性结构—>线性表
数据结构基础:P2.2-线性结构—>堆栈
数据结构基础:P2.3-线性结构—>队列
数据结构基础:P2.4-线性结构—>应用实例:多项式加法运算
数据结构基础:P2.5-线性结构—>应用实例:多项式乘法与加法运算-C实现
数据结构基础:P3.1-树(一)—>树与树的表示
数据结构基础:P3.2-树(一)—>二叉树及存储结构
数据结构基础:P3.3-树(一)—>二叉树的遍历
数据结构基础:P3.4-树(一)—>小白专场:树的同构-C语言实现
数据结构基础:P4.1-树(二)—>二叉搜索树
数据结构基础:P4.2-树(二)—>二叉平衡树
数据结构基础:P4.3-树(二)—>小白专场:是否同一棵二叉搜索树-C实现
数据结构基础:P4.4-树(二)—>线性结构之习题选讲:逆转链表
数据结构基础:P5.1-树(三)—>堆
数据结构基础:P5.2-树(三)—>哈夫曼树与哈夫曼编码
数据结构基础:P5.3-树(三)—>集合及运算
数据结构基础:P5.4-树(三)—>入门专场:堆中的路径
数据结构基础:P5.5-树(三)—>入门专场:File Transfer
数据结构基础:P6.1-图(一)—>什么是图
数据结构基础:P6.2-图(一)—>图的遍历
数据结构基础:P6.3-图(一)—>应用实例:拯救007
数据结构基础:P6.4-图(一)—>应用实例:六度空间
数据结构基础:P6.5-图(一)—>小白专场:如何建立图-C语言实现
数据结构基础:P7.1-图(二)—>树之习题选讲:Tree Traversals Again
数据结构基础:P7.2-图(二)—>树之习题选讲:Complete Binary Search Tree
数据结构基础:P7.3-图(二)—>树之习题选讲:Huffman Codes
数据结构基础:P7.4-图(二)—>最短路径问题
数据结构基础:P7.5-图(二)—>哈利·波特的考试
数据结构基础:P8.1-图(三)—>最小生成树问题
数据结构基础:P8.2-图(三)—>拓扑排序

文章目录

  • 一、题目描述
  • 二、思路分析
  • 三、整体代码
  • 四、其它类似问题


一、题目描述

题目描述:
有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。

输入格式:
输入说明:输入数据的第1行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号0~(N−1)M高速公路的条数S出发地的城市编号D目的地的城市编号。随后的M行中,每行给出一条高速公路的信息,分别是:城市1、城市2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过500。输入保证解的存在。

输出格式:
在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。

输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20

输出样例:
3 40


二、思路分析

这道题看上去比较直白,一看我们就知道这是一个图论的问题,一看就知道这是一个单元最短路径的问题,一看就知道是可以用Dijkstra这个算法来解决的问题。但是如果直接把Dijkstra算法复制粘贴上去,解决不了这个问题,因为他多了一点条件。

这道题里面,城市是作为图里面的结点,而公路是作为图里面的边。麻烦的地方是在于每一条边的权重不止一种,第一种权重是距离,另外一种权重是收费。这个题目要求的是,首先我是以距离为标准来判断最短路的,我要找一个距离意义上的最短路径。当这样的路径不止一条的时候,那我就需要找权重2最小的,也就是收费最便宜的那条路径。
首先我们看一下输入:
数据结构基础:P8.3-图(三)--->图之习题选讲-旅游规划_第1张图片
接着构造出相应的网络
数据结构基础:P8.3-图(三)--->图之习题选讲-旅游规划_第2张图片
最短距离的路径有两条,一个从0到1到3,另外一个从0到2到3,这两条路径的距离都是等于3。在距离并列的情况下,我们才去比较他们的收费,然后我们发现收费是40和50,所以最后我们输出的选择应该是0到2到3
数据结构基础:P8.3-图(三)--->图之习题选讲-旅游规划_第3张图片

解决问题的方法肯定还是要用 Dijkstra 的算法,或者说至少是基于Dijkstra 的算法,他仍然是一个单元最短路的问题。Dijkstra算法是一个结点一个结点往那个集合里面去收集的,每当收集进来一个结点的时候他要检查一下其他结点距离有没有被影响,会得到一个更短的距离。如果更短的话我来刷新这个距离,如果是等长的话我就什么都不做了。那关键的区别就在当距离相等的时候我不能什么都不做,我还需要按照收费来做一个更新。

我们接下去来看核心的算法,首先呢我们来回忆一下经典的Dijkstra算法长什么样。

void Dijkstra( Vertex s )
{ 
	while (1) {
		V = 未收录顶点中dist最小者;
		if ( 这样的V不存在 )
			break; 
		collected[V] = true;
		for ( V 的每个邻接点 W )
			if ( collected[W] == false ) 
				if ( dist[V]+E<V,W> < dist[W] ) {
					dist[W] = dist[V] + E<V,W> ;
					path[W] = V;
					cost[W] = cost[V] + C<V,W> ;
				}
			else if ( (dist[V]+E<V,W> == dist[W]) && (cost[V]+C<V,W> < cost[W]) ){
				cost[W] = cost[V] + C<V,W> ;
				path[W] = V;
	}
}

三、整体代码

整体代码如下,可以参考这篇博客,代码思路和上面讲解的一致:旅游规划问题

#define _CRT_SECURE_NO_WARNINGS
#include 

#define MAXN 500
#define ERROR -1
#define Infinite 65534

int N, M, S, D;//城市的个数 高速公路的条数 出发地 目的地
int Dist[MAXN][MAXN], Cost[MAXN][MAXN];//距离与花费矩阵
int dist[MAXN], cost[MAXN], collected[MAXN];//最短距离与花费 标记数组

void Inicialization(void);
void Dijkstra(void);
int FindMinWay(void);

int main()
{
	scanf("%d %d %d %d", &N, &M, &S, &D);//城市的个数 高速公路的条数 出发地 目的地
	Inicialization();//初始化
	Dijkstra();
	printf("%d %d", dist[D], cost[D]);
	return 0;
}

void Inicialization(void)
{
	for (int i = 0; i < N; i++)
		for (int j = 0; j < N; j++)
			Dist[i][j] = Cost[i][j] = Infinite; //矩阵初始化为无限值

	int v1, v2, d, c;
	for (int i = 0; i < M; i++)
	{
		scanf("%d %d %d %d", &v1, &v2, &d, &c);
		Dist[v1][v2] = Dist[v2][v1] = d;//输入距离路径
		Cost[v1][v2] = Cost[v2][v1] = c;//输入花费路径
	}

	for (int i = 0; i < N; i++)
		dist[i] = cost[i] = Infinite;//矩阵初始化为无限值
}

void Dijkstra(void)
{
	dist[S] = cost[S] = 0;//出发地为0
	collected[S] = 1;//出发地访问标记  
	int v;
	for (int i = 0; i < N; i++){
		if (!collected[i] && Dist[S][i] < Infinite) //如果没访问 且有路径
		{
			dist[i] = Dist[S][i];
			cost[i] = Cost[S][i];
		}
	}//记录出发地直达的路径

	while (1)
	{
		v = FindMinWay();//找出最短出发地直达且未访问的城市
		if (v == ERROR) break;
		collected[v] = 1;//找出城市的访问标记

		for (int i = 0; i < N; i++)//循环每个城市
			if (!collected[i] && Dist[v][i] < Infinite)//如果未访问且有路径
				if ((dist[v] + Dist[v][i] < dist[i]) || (dist[v] + Dist[v][i] == dist[i] && cost[v] + Cost[v][i] < cost[i]))
				{//如果从先到该城市再到另一城市距离小于直接到另一城市
				 //或者从先到该城市再到另一城市距离等于直接到另一城市,且花费少
					dist[i] = dist[v] + Dist[v][i];//更新最短路径
					cost[i] = cost[v] + Cost[v][i];
				}
	}
}

int FindMinWay(void)
{
	int min = Infinite;
	int temp;

	for (int i = 0; i < N; i++)//循环每个城市 找出最短的路径
		if (!collected[i] && dist[i] < min)
		{
			min = dist[i];
			temp = i;
		}
	if (min == Infinite) return ERROR;
	return temp;
}

运行,输入题目中的输入样例,结果如下。
数据结构基础:P8.3-图(三)--->图之习题选讲-旅游规划_第4张图片


四、其它类似问题

Dijkstra算法还有各种变形的应用,比较常见的有以下两种:
①要求数最短路径有多少条

count[s] = 1;
//count[v] 记录从原点到v的最短路径一共有多少条
如果找到更短路:count[W]=count[V]; //直接替换
如果找到等长路:count[W]+=count[V]; //相加,因为两条路分别都有若干种走法

②要求边数最少的最短路

count[s] = 0; 
如果找到更短路:count[W]=count[V]+1;
如果找到等长路:count[W]=count[V]+1

你可能感兴趣的:(数据结构基础,数据结构,旅游,算法,c语言,c算法)