算法 图6 旅游规划 (25point(s))

全部每周作业和视频思考题答案和解析 见 浙江大学 数据结构 思考题+每周练习答案汇总

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

输入格式:

输入说明:输入数据的第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

解答:

毕竟前面都是用的邻接矩阵来做的,这次为了练习一下邻接表,改用邻接表来做。

首先写一个程序基本结构:

#include <iostream>
using namespace std;


int main(void) {

	system("pause");
	return 0;
}

然后。。。慢着。慢着。。。

好像用邻接表来实现Dijkstra算法不太方便啊,毕竟邻接表一般是用来处理类似于DFS这种可以按链遍历的算法,而Dijkstra需要能随时访问任意的Graph[V][K],所以就不是很方便了。

单独写了一个邻接表实现DFS算法的程序,放在这里:

数据结构 邻接表构造图用来实现DFS算法

扫描二维码关注公众号,回复: 10618527 查看本文章

改用邻接矩阵吧。

先完成一个最基本的Dijkstra找最短路径的程序:

#include <iostream>
using namespace std;
#define MYINFINITY 6000000
#define MaxVertexNum 500
struct Edge{
	int length; //公路长度
	int price; //收费
};
Edge Graph[MaxVertexNum][MaxVertexNum];
bool collected[MaxVertexNum];
int dist[MaxVertexNum];
int path[MaxVertexNum];

void initGraphdata(int N,int E);
bool Dijkstra(int N, int S, int A);
int main(void) {
	int N, E; //顶点数,边数
	cin >> N >> E;
	int S, A; //出发地,目的地
	cin >> S >> A;
	initGraphdata(N,E);
	
	Dijkstra(N, S,A);
	
	system("pause");
	return 0;
}

void initGraphdata(int N, int E) {
	for (int i = 0;i < N;i++) {
		for (int j = 0;j < N;j++) {
			if (i == j) {
				Graph[i][j].length = 0;
				Graph[i][j].price = 0;
			}
			else {
				Graph[i][j].length = MYINFINITY;
				Graph[i][j].price = MYINFINITY;
			}
		}
	}
	int v1, v2, length, price;
	for (int i = 0;i < E;i++) {
		cin >> v1 >> v2 >> length >> price;
		Graph[v1][v2].length = length;
		Graph[v2][v1].length = length;
		Graph[v1][v2].price = price;
		Graph[v2][v1].price = price;
	}
	//打印输出一下
	//for (int i = 0;i < N;i++) {
	//	for (int j = 0;j < N;j++) {
	//		cout << Graph[i][j].length << " ";
	//	}cout << endl;
	//}cout << endl;
}

// 邻接矩阵存储 - 有权图的单源最短路算法 
int FindMinDist(int N)
{ // 返回未被收录顶点中dist最小者 
	int MinV;
	int MinDist = MYINFINITY;
	//cout << "dadada" << endl;
	for (int V = 0; V<N; V++) {
		if (collected[V] == false && dist[V]<MinDist) {
			// 若V未被收录,且dist[V]更小 
			MinDist = dist[V]; // 更新最小距离 
			MinV = V; // 更新对应顶点 
		}
	}
	if (MinDist < MYINFINITY) // 若找到最小dist 
		return MinV; // 返回对应的顶点下标 
	else return -1;  // 若这样的顶点不存在,返回错误标记 
}

bool Dijkstra(int N, int S,int A)
{
	//之前修改程序的时候不小心在这里面定义了一个collected数组[MaxVertexNum];
	//导致全局的collected一直没有被更新。

	// 初始化:此处默认邻接矩阵中不存在的边用INFINITY表示 
	for (int V = 0; V<N; V++) {
		dist[V] = Graph[S][V].length;
		if (dist[V]<MYINFINITY)
			path[V] = S;
		else
			path[V] = -1;
		collected[V] = false;
	}
	// 先将起点收入集合 
	dist[S] = 0;
	collected[S] = true;
	int V, W;
	while (1) {
		// V = 未被收录顶点中dist最小者 
		V = FindMinDist(N);
		if (V == -1) // 若这样的V不存在 
			break;      // 算法结束 
		collected[V] = true;  // 收录V 
		for (W = 0; W<N; W++) // 对图中的每个顶点W 
			// 若W是V的邻接点并且未被收录 
			if (collected[W] == false && Graph[V][W].length<MYINFINITY) {
				//if (Graph[V][W].length<0)return false;// 若有负边,不能正确解决,返回错误标记 
				// 若收录V使得dist[W]变小 
				if (dist[V] + Graph[V][W].length < dist[W]) {
					dist[W] = dist[V] + Graph[V][W].length; // 更新dist[W] 
					path[W] = V; // 更新S到W的路径 
				}
			}
		
	} // while结束
	cout << dist[A];
	return true; // 算法执行完毕,返回正确标记 
}

输入题目中给的数据以后,输出3。

现在再思考如何在多个最短路径中找到钱最少的路径。

我发现在找最短路径的算法中,有这么一句:

if (dist[V] + Graph[V][W].length < dist[W]) {
	dist[W] = dist[V] + Graph[V][W].length; // 更新dist[W] 
	path[W] = V; // 更新S到W的路径 
}

我们是不是可以这么判断:如果dist[V] + Graph[V][W].length <= dist[W],则更新为钱更少的那个路径呢?

故我们需要一个数组来存钱。

int wholePrice[MaxVertexNum];

注意需要把wholePrice全都初始化为MYINFINITY,并且出发地的 wholePrice[S] = 0;

然后中间的判断语句我们需要进行修改:

if (dist[V] + Graph[V][W].length < dist[W]) {
	wholePrice[W] = wholePrice[V] + Graph[V][W].price;
	dist[W] = dist[V] + Graph[V][W].length; // 更新dist[W] 
	path[W] = V; // 更新S到W的路径 
}else if ((dist[V] + Graph[V][W].length == dist[W]) && 
(wholePrice[V] + Graph[V][W].price < wholePrice[W])) {
	wholePrice[W] = wholePrice[V] + Graph[V][W].price;
}

如果距离更小,则二话不说,更新花费和长度,如果距离一样但是花费不同都一样,则更新为那个花费低的价格(因为不需要记录路径,所以不用更新路径)

有人可能会问,这样更新会不会遗漏某些情况:其实不会。因为Dijkstra算法是从起点开始一直去更新最近路径,所以中间会把所有两个顶点之间的距离都过滤一遍,所以相等的最小距离也都会被遍历到,所以不会遗漏。

全部代码粘贴如下:

#include <iostream>
using namespace std;
#define MYINFINITY 6000000
#define MaxVertexNum 500
struct Edge{
	int length; //公路长度
	int price; //收费
};
Edge Graph[MaxVertexNum][MaxVertexNum];
bool collected[MaxVertexNum];
int dist[MaxVertexNum];
int path[MaxVertexNum];
int wholePrice[MaxVertexNum];
int priceRecord[MaxVertexNum];

void initGraphdata(int N,int E);
bool Dijkstra(int N, int S, int A);
int main(void) {
	int N, E; //顶点数,边数
	cin >> N >> E;
	int S, A; //出发地,目的地
	cin >> S >> A;
	initGraphdata(N,E);
	Dijkstra(N, S,A);
	
	system("pause");
	return 0;
}

void initGraphdata(int N, int E) {
	for (int i = 0;i < N;i++) {
		for (int j = 0;j < N;j++) {
			if (i == j) {
				Graph[i][j].length = 0;
				Graph[i][j].price = 0;
			}
			else {
				Graph[i][j].length = MYINFINITY;
				Graph[i][j].price = MYINFINITY;
			}
		}
	}
	int v1, v2, length, price;
	for (int i = 0;i < E;i++) {
		cin >> v1 >> v2 >> length >> price;
		Graph[v1][v2].length = length;
		Graph[v2][v1].length = length;
		Graph[v1][v2].price = price;
		Graph[v2][v1].price = price;
	}
	//打印输出一下
	//for (int i = 0;i < N;i++) {
	//	for (int j = 0;j < N;j++) {
	//		cout << Graph[i][j].price << " ";
	//	}cout << endl;
	//}cout << endl;
}

// 邻接矩阵存储 - 有权图的单源最短路算法 
int FindMinDist(int N)
{ // 返回未被收录顶点中dist最小者 
	int MinV;
	int MinDist = MYINFINITY;
	//cout << "dadada" << endl;
	for (int V = 0; V<N; V++) {
		if (collected[V] == false && dist[V]<MinDist) {
			// 若V未被收录,且dist[V]更小 
			MinDist = dist[V]; // 更新最小距离 
			MinV = V; // 更新对应顶点 
		}
	}
	if (MinDist < MYINFINITY) // 若找到最小dist 
		return MinV; // 返回对应的顶点下标 
	else return -1;  // 若这样的顶点不存在,返回错误标记 
}

bool Dijkstra(int N, int S,int A)
{
	//之前修改程序的时候不小心在这里面定义了一个collected数组[MaxVertexNum];
	//导致全局的collected一直没有被更新。

	// 初始化:此处默认邻接矩阵中不存在的边用INFINITY表示 
	for (int V = 0; V<N; V++) {
		dist[V] = Graph[S][V].length;
		wholePrice[V] = Graph[S][V].price;
		priceRecord[V] = 0;
		if (dist[V]<MYINFINITY)
			path[V] = S;
		else
			path[V] = -1;
		collected[V] = false;
		
	}
	// 先将起点收入集合 
	dist[S] = 0;
	collected[S] = true;
	wholePrice[S] = 0;
	priceRecord[S] = 1;
	int V, W;
	while (1) {
		// V = 未被收录顶点中dist最小者 
		V = FindMinDist(N);
		if (V == -1) // 若这样的V不存在 
			break;      // 算法结束 
		collected[V] = true;  // 收录V 
		for (W = 0; W<N; W++) // 对图中的每个顶点W 
			// 若W是V的邻接点并且未被收录 
			if (collected[W] == false && Graph[V][W].length<MYINFINITY) {
				//if (Graph[V][W].length<0)return false;// 若有负边,不能正确解决,返回错误标记 
				// 若收录V使得dist[W]变小 
				if (dist[V] + Graph[V][W].length < dist[W]) {
					wholePrice[W] = wholePrice[V] + Graph[V][W].price;
					dist[W] = dist[V] + Graph[V][W].length; // 更新dist[W] 
					path[W] = V; // 更新S到W的路径 

				}else if ((dist[V] + Graph[V][W].length == dist[W]) && (wholePrice[V] + Graph[V][W].price < wholePrice[W])) {
					wholePrice[W] = wholePrice[V] + Graph[V][W].price;
				}
			}
		
	} // while结束
	cout << dist[A] << " "<< wholePrice[A];
	return true; // 算法执行完毕,返回正确标记 
}

测试结果:

发布了174 篇原创文章 · 获赞 394 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/tiao_god/article/details/105303032