赛事系统为参赛者提供赛地的校园导游程序。为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供图中任意目标地(建筑物)的问路查询,即查询任意两个目的地(建筑物)之间的一条最短的简单路径。
5.1问题分析
校园导游程序:需要设计一个校园导游程序,为参赛者提供路径导航的查询服务。参赛者可以查询校园地图中的目标地(建筑物)相关信息,并且可以查询任意两个目的地之间的最短路径。
目标地的导航查询:为了提供导航查询服务,需要建立校园地图的数据结构,并记录不少于10个目标地(建筑物)的信息。可以使用图(Graph)数据结构来表示校园地图,其中节点表示建筑物,边表示建筑物之间的路径。
最短路径查询:参赛者可以查询任意两个目的地之间的最短路径,即从起点建筑物到终点建筑物的一条最短的简单路径。需要实现相应的算法,如Dijkstra算法或BFS算法,来计算最短路径。这里我们采用Dijkstra算法:
1.创建一个空的最短路径集合,用于存储已经确定最短路径的顶点。
2.初始化起点的最短距离为0,其他顶点的最短距离为无穷大。
3.选择起点作为当前顶点,并将其加入最短路径集合。
4.对于起点的每个邻居顶点,更新其最短距离。如果通过当前顶点到达邻居顶点的距离更短,则更新邻居顶点的最短距离。
5.从未加入最短路径集合的顶点中选择一个最短距离的顶点作为当前顶点,并将其加入最短路径集合。
6.重复步骤4和步骤5,直到所有顶点都加入最短路径集合或者没有可选顶点。
7.最短路径集合中的顶点及其最短距离即为起点到各个顶点的最短路径。
5.2 算法设计
首先定义一个名为 Building 的结构体,通过这个结构体,可以方便地表示建筑物及其相邻关系,以便后续的路径导航和查询操作,每个建筑物对象都可以包含一个 neighbors 向量,其中存储了与该建筑物相邻的其他建筑物及其路径长度
struct Building {
std::string name;
std::vector<std::pair<std::string, int>> neighbors;
};
然后定义一个校园地图类,类中有一个私有成员变量buildings,该无序映射 buildings 将建筑物的名称作为键,与对应的 Building 对象关联起来。通过使用建筑物的名称作为键,可以快速查找和访问对应的建筑物对象。
buildings 映射存储了校园地图中所有建筑物的信息,可以通过它来添加建筑物、建立建筑物之间的连接关系,以及进行路径导航和查询操作。
class CampusMap {
private:
std::unordered_map<std::string, Building> buildings;
public:
// 添加建筑物
void addBuilding(const std::string& name) {
Building building;
building.name = name;
buildings[name] = building;
}
创建一个Building对象building,然后将传入的建筑物名称赋值给 building 对象的 name 成员变量。
将建筑物的名称作为键,与对应的Building对象关联起来
通过调用该函数,可以将新的建筑物添加到校园地图的 buildings 中,以便后续进行建筑物之间的连接关系和路径导航查询。
// 添加建筑物之间的连接关系
void addConnection(const std::string& building1, const std::string& building2, int distance) {
buildings[building1].neighbors.push_back({ building2, distance });
buildings[building2].neighbors.push_back({ building1, distance });
}
这么做的目的是确定两个建筑之间的连接关系,避免出现重复或错误
std::vector<std::string> getShortestPath(const std::string& start, const std::string& end) {
std::unordered_map<std::string, std::string> previous;//用于记录每个建筑物的前一个建筑物在最短路径中的位置
std::unordered_map<std::string, int> distance;//记录从起始建筑物到每个建筑物的距离
std::priority_queue<std::pair<int, std::string>, std::vector<std::pair<int, std::string>>, std::greater<>> pq;
在函数内部,首先定义了两个容器 previous 和 distance。previous用于记录每个建筑物的前一个建筑物在最短路径中的位置。distance用于记录从起始建筑物到每个建筑物的距离。
然后,定义了一个优先队列 pq,其元素类型是 std::pair<int, std::string>,按照距离的升序进行排序。这里使用 std::greater<> 作为比较器,确保距离小的元素在队列前面。
for (const auto& pair : buildings) {
const std::string& building = pair.first;
distance[building] = std::numeric_limits<int>::max();
}
初始化距离为无穷大,表示未知距离
// 设置起点的距离为0
distance[start] = 0;
pq.push({ 0, start });
while (!pq.empty()) {
const std::string current = pq.top().second;//在每次循环中,首先从优先队列中取出距离最小的建筑物current,并将其从队列中移除
pq.pop();
if (current == end) {
break; // 找到最短路径,结束循环
}
const int currentDistance = distance[current];
for (const auto& neighbor : buildings[current].neighbors) {
const std::string& neighborName = neighbor.first;//邻居建筑物的名称
const int neighborDistance = neighbor.second;//与邻居建筑物之间的距离
const int newDistance = currentDistance + neighborDistance;
if (newDistance < distance[neighborName]) {
distance[neighborName] = newDistance;
previous[neighborName] = current;
pq.push({ newDistance, neighborName });
}
}
}
使用 Dijkstra 算法来计算最短路径。首先,将起始建筑物的距离设为 0,并将其加入优先队列 pq 中。接下来是一个主要的循环,循环条件是优先队列非空。在每次循环中,从优先队列中取出距离最小的建筑物 current,并将其从队列中移除。如果 current 等于目标建筑物 end,表示找到了最短路径,结束循环。然后,获取当前建筑物 current 的距离,并遍历其相邻建筑物。
对于每个相邻建筑物 neighbor,计算新的距离 newDistance,即当前距离加上当前建筑物与相邻建筑物之间的距离。如果新的距离 newDistance 小于相邻建筑物的当前距离,说明找到了一条更短的路径,更新相邻建筑物的距离和前一个建筑物,并将其加入优先队列中。
通过以上步骤,循环处理直到队列为空或找到最短路径。最后,根据 previous 和 distance 的记录,构建最短路径,并返回结果。通过调用该函数,可以获取两个建筑物之间的最短路径,并进行路径导航查询。
// 构建最短路径
std::vector<std::string> path;
std::string current = end;
path.push_back(current);//将目标建筑物添加在队伍最后
while (current != start) {
current = previous[current];
path.push_back(current);
}
std::reverse(path.begin(), path.end());
return path;
在这里创建了一个空的 path 向量,用于存储最短路径的建筑物顺序。然后,将目标建筑物 end 添加到 path 的末尾。接下来,使用一个循环,从目标建筑物开始,通过 previous 映射找到每个建筑物在最短路径中的前一个建筑物,将其添加到 path 中。最后,将 path 中的顺序反转,以得到正确的最短路径顺序,然后返回 path。
这段代码的作用是根据 previous 映射构建最短路径,并以正确的顺序返回最短路径的建筑物列表。