华为2025年校招笔试手撕真题教程(二)
一、题目
大湾区某城市地铁线路非常密集,乘客很难一眼看出选择哪条线路乘坐比较合适,为了解决这个问题,地铁公司希望你开发一个程序帮助乘客挑选合适的乘坐线路,使得乘坐时间最短,地铁公司可以提供的数据是各相邻站点之间的乘坐时间。
解答要求
时间限制:C/C++1000ms,其他语言:2000ms内存限制:C/C++256MB,其他语言:512MB
二、分析
该问题要求开发一个程序帮助乘客选择乘坐时间最短的地铁线路,地铁公司提供的数据是各相邻站点之间的乘坐时间。这是一个典型的最短路径问题,可以借助图论中的算法来解决。首先,把整个地铁网络抽象为一个有向图,图中的节点代表地铁站点,边代表站点之间的连接(线路段),边上的权重则表示该段的乘坐时间。对于换乘的情况,可以视为通过换乘站这一节点,将不同线路的站点连接起来,即换乘站作为一个特殊节点,其出边可以连接到其他线路的相邻站点,且换乘时间可以当作特殊边的权重(若换乘需要额外时间则需考虑,否则权重为0)。
接下来,需要明确输入数据的格式。通常,可能需要输入站点数量、线路数量,以及每条线路的站点顺序和相邻站点之间的行驶时间。例如,对于一条包含多个站点的线路,依次给出每两个相邻站点之间的时间。此外,还需明确换乘站之间的换乘时间,或者默认换乘时间为0(即在换乘站直接换乘到其他线路无额外时间开销)。针对这一问题,Dijkstra算法是一个合适的选择。因为Dijkstra算法能够高效地找到加权图中两点之间的最短路径,假设地铁乘坐时间都是非负的,这满足Dijkstra算法的适用条件。
具体实现时,首先要构建图结构。使用邻接表或邻接矩阵来表示图。邻接表在稀疏图中更为高效,而邻接矩阵在稠密图中查询速度快。对于地铁网络,通常站点数量较多但每个站点的相邻站点数量有限(尤其是换乘站可能连接多条线路),所以邻接表可能是更好的选择。每个节点的邻接列表中存储了其相邻站点以及对应的乘坐时间(权重)。然后,读取所有线路信息以及相邻站点时间,并构建邻接表。对于每条线路,依次将相邻站点之间的连接加入图中。同时,处理换乘站之间的连接,把换乘视为从一个站点到其他线路的相邻站点的边(如果允许直接换乘到下一站而无需重新进站等),或者更可能的是,换乘站本身作为一个节点,其邻接站点包括同一线路的前后站点以及其他线路在该换乘站的站点。
用户输入起点和终点后,程序以起点作为源节点,运行Dijkstra算法计算到各个站点的最短时间。Dijkstra算法的核心是维护一个距离数组,记录从起点到每个节点的当前最短距离,并使用优先队列(通常是最小堆)来选择下一个要处理的节点。每次从优先队列中取出距离起点最近的节点,更新其邻接节点的距离。重复这一过程直到处理完所有节点或者找到终点为止。算法结束后,距离数组中终点对应的值即为最短乘坐时间。如果终点不可达(距离仍为初始的无穷大值),则输出无法到达。
此外,需要考虑效率问题。因为地铁线路可能非常密集,站点数量庞大,所以算法的时间复杂度和空间复杂度需要控制在合理范围内。Dijkstra算法的时间复杂度在使用优先队列实现时为O(M + N log N),其中M是边数,N是节点数。对于大规模的地铁网络,这应该是可行的,但需要确保数据结构的高效性,例如使用堆优化的Dijkstra算法。
可能的难点在于处理换乘情况。换乘实际上涉及多个线路之间的连接,需要确保图的构建能够正确反映换乘关系。例如,当乘客在换乘站换乘到另一条线路时,该换乘是否算作一个站点到另一个站点的边,还是换乘站作为一个中间节点连接不同线路的站点。一般来说,更合理的模型是将换乘站视为一个节点,其连接同一线路的前后站点以及不同线路在该换乘站的站点。例如,换乘站S属于线路A和线路B,那么在图中,S会有边连接到线路A的前一站和后一站,以及线路B的前一站和后一站,边的权重分别为对应的行驶时间。
另外,输入数据的处理需要仔细设计。例如,每条线路的站点可能以顺序给出,相邻站点之间的时间依次给出。需要将这些信息正确地转换为图中的边。例如,对于线路站点列表[S1, S2, S3, ..., Sn]和时间列表[t1, t2, ..., t(n-1)],则S1到S2的时间是t1,S2到S3的时间是t2,依此类推。同时,如果是双向线路(地铁通常是双向运行的),则需要为每个方向都添加边,即S2到S1的时间也是t1,除非题目说明线路是单向的。
三、代码
由于目前无法确定具体的输入数据格式和细节(如站点如何标识、换乘站的表示方式等),我将基于常见的输入方式提供一个通用的示例代码。以下代码使用Python实现,并基于Dijkstra算法。
import sys
import heapqclass MetroNetwork:def __init__(self):self.adjacency_list = {}def add_station(self, station):if station not in self.adjacency_list:self.adjacency_list[station] = []def add_connection(self, from_station, to_station, time):self.adjacency_list[from_station].append((to_station, time))def dijkstra(self, start, end):# 初始化距离字典distances = {station: float('infinity') for station in self.adjacency_list}distances[start] = 0# 优先队列,存储(距离,节点)priority_queue = [(0, start)]while priority_queue:current_distance, current_station = heapq.heappop(priority_queue)# 如果已经到达终点,可以直接返回if current_station == end:return current_distance# 如果当前距离大于已知的最短距离,则跳过if current_distance > distances[current_station]:continuefor neighbor, time in self.adjacency_list[current_station]:distance = current_distance + timeif distance < distances[neighbor]:distances[neighbor] = distanceheapq.heappush(priority_queue, (distance, neighbor))# 如果无法到达终点,返回-1或相应提示return distances[end] if distances[end] != float('infinity') else -1def main():# 读取输入input_lines = sys.stdin.read().splitlines()# 第一行是站点和线路信息# 假设第一行是站点数和线路数,接下来是线路的站点和时间信息# 这里需要根据实际输入格式调整# 示例输入格式(仅供参考):# 第一行:2 # 表示两个站点# 第二行:3 # 表示三条线路# 接下来每行是一条线路的站点和时间,如:A B 5 表示A到B需要5分钟# ...# 这里需要根据实际输入格式调整metro = MetroNetwork()# 这里是一个简单的示例,实际需要根据输入格式调整for line in input_lines[1:]: # 跳过第一行parts = line.strip().split()if len(parts) >= 3:from_station = parts[0]to_station = parts[1]time = int(parts[2])metro.add_station(from_station)metro.add_station(to_station)metro.add_connection(from_station, to_station, time)# 如果是双向的,添加反向连接metro.add_connection(to_station, from_station, time)# 用户输入起点和终点start_station = input("请输入起点站点: ")end_station = input("请输入终点站点: ")# 找到最短路径时间shortest_time = metro.dijkstra(start_station, end_station)if shortest_time != -1:print(f"从{start_station}到{end_station}的最短乘坐时间是: {shortest_time} 分钟")else:print("无法到达目的地")if __name__ == "__main__":main()