题目:爷爷奶奶的幸福生活
【问题描述】
张三和李四夫妇退休后,去国外风景秀美的H市买了一套房子准备安度晚年。住了一段时间后,他们对当地的交通还是不太了解,有时很郁闷,想去一个地方又不知道应该乘什么公交车,在什么地方转车,在什么地方下车。请你写一个程序帮助他用最短的时间到达目的地(假设每一路公交车都只在起点站和终点站停,而且随时都会开)。
【输入形式】
第一行是公交车的总数N(0<=N<=10000);
第二行是张三的所在地start,目的地end;
接着有n行,每行有站名s,站名e,以及从s到e的时间整数t(0<t<100)(每个地名是一个长度不超过30的字符串)。
note:一组数据中地名数不会超过150个。
【输出形式】
如果张三能到达目的地,第一行输出最短的时间,第二行输出最短时间的路径;否则,输出“No path.”。
【样例输入】
5
Leeds Edinburgh
Glasgow Edinburgh 70
Leeds Glasgow 10
Bristol Edinburgh 40
Leeds Edinburgh 65
Leeds Bristol 20
【样例输出】
60
Leeds -> Bristol ->Edinburgh
后续:虽然偶尔会迷路,但是因为有了你的帮助,张三和李四从此还是过上了幸福的生活。
――全剧终――
算法思路:
Dijstra算法用来求一个顶点到其他顶点间的最短路径,而Floyd算法适用于求任意两个点间的最短路径。
Dijstra可用类,也可用结构体。本文为简化代码选择结构体,有时间的话会补充类的代码。
这个题的一个难点在于如何将字符串存入顶点数组中,我们采用string数组来解决这个问题。
定义S集合和U集合,每次循环一遍,找到U集合中dist数组中最小值点k加入到S集合中,比较起点是否能通过从起点到k再到终点找到更短的路径,若找得到则更新dist和path数组。
时间复杂度为O(n2)。
推荐视频: 最短路径(Dijskra算法) 作者:懒猫老师 https://www.bilibili.com/read/cv8013121/?from=search&spm_id_from=333.337.0.0 出处:bilibili。
#include <bits/stdc++.h>
#include <iostream>
#include <string.h>
#define INF 0x3f3f3f3f
#define MAX 32
using namespace std;
//邻接矩阵图
struct MGraph
{
int vexNum; // 顶点个数
string vertex[5*MAX]; // 存储顶点信息,即每个公交站的名字
int arcNum; // 边的个数
int arc[MAX][MAX]; // 邻接矩阵
};
//Dijstra结构体
struct Dijstra
{
int s[MAX]={0}; // S集合
int dist[MAX]={0}; // dist数组存储源点到各顶点的距离
int path[MAX]={0}; // path数组存储该点上一个顶点的下标
string start, end; // 张三的起点和终点
};
bool isdiffer(string str, string arr[], int n); // 判断字符串是否和字符串数组中的元素重复,即获得vertex数组
int getIndex(string str, string arr[], int n); // 获取vertex的下标
int getMin(int *s, int dist[], int n); // 获得U集合里元素dist的最小值
void inDijstra(MGraph G, Dijstra &D); // Dijstra算法,获得dist和path数组
bool input(MGraph &G, Dijstra &D); // 输入函数
void display(MGraph G, Dijstra D); // 输出答案
/**判断字符串是否和字符串数组中的元素重复,用于获得vertex数组*/
bool isdiffer(string str, string arr[], int n)
{
bool flag = true;
for(int i=0; i<n; i++)
{
if(str == arr[i])
{
flag = false;
break;
}
}
return flag;
}
/**获取vertex的下标*/
int getIndex(string str, string arr[], int n)
{
for(int i=0; i<n; i++)
{
if(str == arr[i])
return i;
}
return -1;
}
/**获得U集合里元素dist的最小值*/
int getMin(int *s, int dist[], int n)
{
int min = INF;
int minindex = -1;
for(int i=0; i<n; i++)
{
// 如果在U集合中且值小于min
if(s[i] == 0 && dist[i] < min)
{
min = dist[i];
minindex = i;
}
}
return minindex;
}
/**Dijstra算法,获得dist和path数组*/
void inDijstra(MGraph G, Dijstra &D)
{
int startindex = getIndex(D.start, G.vertex, G.vexNum); // 源点下标,即start对应的vertex数组下标
// S集合初始化
for(int i=0; i<G.vexNum; i++)
{
D.s[i] = 0;
}
D.s[startindex] = 1;
// dist、path数组初始化
for(int i=0; i<G.vexNum; i++)
{
D.dist[i] = G.arc[startindex][i];
if(D.dist[i] != INF)
D.path[i] = startindex;
else
D.path[i] = -1;
}
int n = 1; // 表示加入到S集合中的个数
while(n < G.vexNum) // 有顶点在U集合中则循环
{
// 最小值对应的dist数组的下标
int min = getMin(D.s, D.dist, G.vexNum);
if(min == -1) // 没有找到最小值,说明U集合中的点对应的dist均为INF,即两点之间没有路径
break;
D.s[min] = 1; // 将找到的点加入到S集合中
n++;
// 进入循环,更新dist和path数组
for(int i=0; i<G.vexNum; i++)
{
if(G.arc[min][i] != 0 && G.arc[min][i] != INF)
{
if(D.dist[i] > D.dist[min] + G.arc[min][i])
{
D.dist[i] = D.dist[min] + G.arc[min][i];
D.path[i] = min;
}
}
}
}
}
/**输入函数*/
bool input(MGraph &G, Dijstra &D)
{
int num; // 公交车总数
cin >> num;
// 检查数据是否有效
if(num < 0 || num > 10000)
{
cout << "N is invalid!\n";
return false;
}
G.arcNum = num;
cin >> D.start >> D.end;
// 检查数据是否有效
if(D.start.size() > MAX || D.end.size() > MAX)
{
cout << "Start or end is too long!\n";
return false;
}
string from, to; // 公交站的起点站和终点站
int time; // 公交车从from到to站所花的时间
int v=0;
// G.arc初始化
for(int i=0; i<MAX; i++)
{
for(int j=0; j<MAX; j++)
{
if(i == j)
G.arc[i][j] = 0;
else
G.arc[i][j] = INF;
}
}
// G.vexNum赋初值为0
G.vexNum=0;
for(int i=0; i<num; i++)
{
cin >> from >> to >> time;
// 检查数据是否有效
if(from.size() > MAX || to.size() > MAX)
{
cout << "From or to is too long!\n";
return false;
}
// 如果公交站起点没有出现过,则加入到vertex数组中
if(isdiffer(from, G.vertex, G.vexNum))
{
G.vertex[v++] = from;
G.vexNum++;
}
// 如果公交站终点没有出现过,则加入到vertex数组中
if(isdiffer(to, G.vertex, G.vexNum))
{
G.vertex[v++] = to;
G.vexNum++;
}
// 改变对应的arc数组的值
G.arc[getIndex(from, G.vertex, G.vexNum)][getIndex(to, G.vertex, G.vexNum)] = time;
}
return true;
}
/**输出结果*/
void display(MGraph G, Dijstra D)
{
int endindex = getIndex(D.end, G.vertex, G.vexNum); // 终点下边,即end对应的vertex数组下标
// 若path为-1,则表示没有路径
if(D.path[endindex] == -1)
{
cout << "No path.\n";
return;
}
cout << D.dist[endindex] << endl;
string tmp[G.vexNum]; // 暂存结果
int i = endindex, v=0;
while(i != D.path[i])
{
tmp[v++] = G.vertex[i];
i = D.path[i];
}
i = D.path[i];
cout << G.vertex[i];
// 因为tmp是逆序存入的,所以逆序输出结果
for(int i=v-1; i>=0; i--)
cout << "->" << tmp[i];
}
int main()
{
MGraph G; // 图结构体
Dijstra D; // Dijstra结构体
bool flag = input(G, D); // 调用输入函数
//如果输入有误,则返回0
if(!flag)
return 0;
inDijstra(G, D); // 调用Dijstra函数
display(G, D); // 输出结果
return 0;
}