Leetcode - 公交路线

338 阅读2分钟

这是我参与更文挑战的第 28 天,活动详情查看更文挑战

题目描述

由于求解的目标是最少乘坐的公交车数量,对于同一辆公交车,乘客可以在其路线中的任意车站间无代价地移动,于是我们可以把公交路线当作点。如果两条公交路线有相同车站,则可以在这两条路线间换乘公交车,那么这两条公交路线之间可视作有一条长度为 1 的边。这样建出的图包含的点数即为公交路线的数量,记作 n

完成了建图后,我们需要先明确新的图的起点和终点,然后使用广度优先搜索,计算出的起点和终点的最短路径,从而得到最少换乘次数。

注意到原本的起点车站和终点车站可能同时位于多条公交路线上,因此在新图上可能有多个起点和终点。对于这种情况,我们初始可以同时入队多个点,并在广度优先搜索结束后检查到各个终点的最短路径,取其最小值才是最少换乘次数。

我们遍历所有公交路线,记录每一个车站属于哪些公交路线。然后我们遍历每一个车站,如果有多条公交路线经过该点,则在这些公交路线之间连边。

实际代码中,我们使用哈希映射来实时维护「车站所属公交路线列表」。假设当前枚举到公交路线 i 中的车站 site,此时哈希映射中已记录若干条公交路线经过车站 site,我们只需要让点 i 与这些点公交路线对应的点相连即可。完成了连线后,我们再将公交路线 i 加入到「车站 site 所属公交路线列表」中。

特别地,起点和终点相同时,我们可以直接返回 0

代码

C++

class Solution {
public:
    int numBusesToDestination(vector<vector<int>>& routes, int source, int target) {
        if (source == target) {
            return 0;
        }

        int n = routes.size();
        vector<vector<int>> edge(n, vector<int>(n));
        unordered_map<int, vector<int>> rec;
        for (int i = 0; i < n; i++) {
            for (int site : routes[i]) {
                for (int j : rec[site]) {
                    edge[i][j] = edge[j][i] = true;
                }
                rec[site].push_back(i);
            }
        }

        vector<int> dis(n, -1);
        queue<int> que;
        for (int bus : rec[source]) {
            dis[bus] = 1;
            que.push(bus);
        }
        while (!que.empty()) {
            int x = que.front();
            que.pop();
            for (int y = 0; y < n; y++) {
                if (edge[x][y] && dis[y] == -1) {
                    dis[y] = dis[x] + 1;
                    que.push(y);
                }
            }
        }

        int ret = INT_MAX;
        for (int bus : rec[target]) {
            if (dis[bus] != -1) {
                ret = min(ret, dis[bus]);
            }
        }
        return ret == INT_MAX ? -1 : ret;
    }
};