浙江大学数据结构MOOC-课后习题-第七讲-图6 旅游规划

题目汇总
浙江大学数据结构MOOC-课后习题-拼题A-代码分享-2024

题目描述

在这里插入图片描述

思路分析

题目要求求出最短路径;且在有多条最短路径时,给出花费最小的那一条。这其实是Dijkstra算法的应用变种,在上一道题的基础上做了一些变换。

我最开始的想法是,先求出最短路径,然后再把所有最短路径拿出来计算花费,进行比较;但这样做实在是不好实现且繁琐。
后来想到,可以在求最短路径的过程中,就筛选出距离短且花费便宜节点,因此就有了以下思路:
①将dist数组的类型设为一个结构体,其内包含长度花费(邻接矩阵数组也要设为该类型)
②在Dijkstra算法中,不仅在当遇到距离更短的节点时,更新长度、花费、路径;而且在遇到距离相等且花费更少的节点时,更新花费、路径。
这样最终求得的dist数组path数组中的值,必然是最短且最便宜的。(此结论可由Djkstra算法的特征推导出)

注:
Djkstra算法性质

  • 该算法时将确定了最短路径的顶点逐个地收入集合S中,S初始状态仅包含源点S;
  • 对于那些还未被收入S中的顶点V,定义dist[V]为从源点S到顶点V的最短路径长度,但是该路径仅经过集合S中的顶点(也就是说,它可能不是最终的最短路径)

代码展示

/*
	求指定城市之间的最短路径——>仍然是一个单源最短路径问题——>用有权图还是无权图?有权图,权重设为1
	思路:
		先dijkstar建立dist与path数组。
		有个问题:会不会存在起始点和终点相同,但是中间路径不同的路径的呢?——>有可能,但是Dijkstra算法
		似乎无法实现有多条最短路径的问题
		存在多条最短路径时,就是说一个点可能会走向多个不同的点,所以在dijkstra算法中,一个点可能会有多个
		“前驱”点,即path[i]可能会有多个,感觉没那么容易实现,如果用数组的话,会浪费多余空间,用链表似乎可行
		
		如果用floyd算法,能找到源点到目标点的最短路径
	然后与源点直接相连的点,到目标点的最短路径也能找到;用这种迭代的方法似乎能够找到多条最短路径
	还没有用过floyd算法,那就用一次吧	

	现在看来以上的两种思路并不对,对于floyd来说,这种迭代的方法难以实现
	询问GPT后,考虑使用以下方法
		特殊化一下邻接矩阵的数组元素,使其为结构体,既存储路径长度,又存储最便宜路径
		在使用Dijkstra算法时,如果路径长度相等,但是更便宜,那么最短路径就取这个
*/
#include <stack>
#include <cmath>
#include <cstdlib>
#include <iostream>
#define MAXSIZE 500
#define INFINITY 65535
#define ERROR 65535
typedef int vertex;
/*  */
struct weightType
{
	int length;
	int expense;
};
/* 边节点 */
struct ENode
{
	vertex V1, V2;
	weightType weight;
};
typedef ENode* ptrToENode;
typedef ptrToENode Edge;
/* 图节点 */
struct GNode
{
	int Nv;
	int Ne;
	weightType G[MAXSIZE][MAXSIZE];	//邻接矩阵
};
typedef GNode* ptrToGNode;
typedef ptrToGNode MGraph;

MGraph creatGraph(int Nv)
{
	MGraph G = (MGraph)malloc(sizeof(GNode));
	G->Nv = Nv;

	/* 初始化邻接矩阵 */
	for (vertex i = 0; i < G->Nv; i++)
	{	
		for (vertex j = 0; j < G->Nv; j++)
		{	
			if (i == j)
			{
				G->G[i][j].length = 0;
				G->G[i][j].expense = 0;
			}
			else
			{
				G->G[i][j].length = INFINITY;
				G->G[i][j].expense = INFINITY;
			}
		}
	}
	return G;
}
/* 插入边 */
void InsertEdge(MGraph Graph, Edge E)
{
	/* 插入边 (V1, V2) */
	Graph->G[E->V1][E->V2].length = E->weight.length;
	Graph->G[E->V1][E->V2].expense = E->weight.expense;
	Graph->G[E->V2][E->V1].length = E->weight.length;
	Graph->G[E->V2][E->V1].expense = E->weight.expense;
}
/* 建立完整的图 */
MGraph buildGraph(int Nv, int Ne)
{	
	/* 建立有Nv个顶点但没有边的图 */
	MGraph G = creatGraph(Nv);
	G->Ne = Ne;
	/* 建立边 */
	Edge E = (Edge)malloc(sizeof(ENode));
	for (int i = 0; i < G->Ne; i++)
	{
		std::cin >> E->V1 >> E->V2 >> E->weight.length >> E->weight.expense;
		InsertEdge(G, E);
	}
	return G;
}

vertex findMinDist(MGraph Graph, weightType dist[], bool collected[])
{
	vertex V, minV;
	int minLength = INFINITY;
	int minExpense = INFINITY;
	for (V = 0; V < Graph->Nv; V++)
	{	/* 路径长度更短 或者 路径等于最小长度但是花费更小 */
		if (collected[V] == false && (dist[V].length < minLength || (dist[V].expense < minExpense && dist[V].length == minLength)))
		{
			minLength = dist[V].length;
			minExpense = dist[V].expense;
			minV = V;
		}
	}
	if (minLength < INFINITY)
		return minV;
	else return ERROR; //错误标记
}

/* djikstra */
void Dijkstra(MGraph Graph,vertex S, weightType dist[], int path[])
{
	vertex V, W;
	bool collected[MAXSIZE];
	/* 初始化:此处默认邻接矩阵中不存在的边用INFINITY表示 */
	for (V = 0; V < Graph->Nv; V++)
	{
		dist[V].length = Graph->G[S][V].length;
		dist[V].expense = Graph->G[S][V].expense;
		if (S == V)
			path[V] = -1;
		else if (dist[V].length != INFINITY && dist[V].expense != INFINITY)
			path[V] = S;
		else
			path[V] = -1;
		collected[V] = false;
	}
	/* 将源点收入集合 */
	dist[S].expense = 0;
	dist[S].length = 0;
	collected[S] = true;
	while (1)
	{
		V = findMinDist(Graph, dist, collected);
		if (V == ERROR)
			break;
		collected[V] = true;
		for (W = 0; W < Graph->Nv; W++)
		{	/* 访问V的邻接节点 */
			if (collected[W] == false && Graph->G[V][W].length != INFINITY)
			{	/* 距离更短时,更新长度、花费、路径 */
				if (dist[V].length + Graph->G[V][W].length < dist[W].length)
				{
					dist[W].length = dist[V].length + Graph->G[V][W].length;
					dist[W].expense = dist[V].expense + Graph->G[V][W].expense;
					path[W] = V;
				}
				/* 距离相等,但是花费更少,更新花费、路径 */
				else if (dist[V].length + Graph->G[V][W].length == dist[W].length && dist[V].expense + Graph->G[V][W].expense < dist[W].expense)
				{
					dist[W].expense = dist[V].expense + Graph->G[V][W].expense;
					path[W] = V;
				}
			}
		}
	}
}
int main()
{	
	weightType dist[MAXSIZE];
	int path[MAXSIZE];
	int Nv;
	int Ne;	
	vertex S, D;

	std::cin >> Nv >> Ne >> S >> D;	//N是城市的个数;M是高速公路的条数;S是出发地的城市编号;D是目的地的城市编号
	MGraph Graph = buildGraph(Nv, Ne);
	
	Dijkstra(Graph, S, dist, path);
	
	std::cout << dist[D].length << ' ' << dist[D].expense;
	return 0;
}

心路历程

虽然经过上一题的洗礼,但这道题一开始还是没有头绪…又被自己菜哭了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值