全部每周作业和视频思考题答案和解析 见 浙江大学 数据结构 思考题+每周练习答案汇总
题目:有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。
输入格式:
输入说明:输入数据的第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算法的程序,放在这里:
改用邻接矩阵吧。
先完成一个最基本的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; // 算法执行完毕,返回正确标记
}
测试结果: