题目:
We have a list of bus routes. Each routes[i]
is a bus route that the i-th bus repeats forever. For example if routes[0] = [1, 5, 7]
, this means that the first bus (0-th indexed) travels in the sequence 1->5->7->1->5->7->1->... forever.
We start at bus stop S
(initially not on a bus), and we want to go to bus stop T
. Travelling by buses only, what is the least number of buses we must take to reach our destination? Return -1 if it is not possible.
Example: Input: routes = [[1, 2, 7], [3, 6, 7]] S = 1 T = 6 Output: 2 Explanation: The best strategy is take the first bus to the bus stop 7, then take the second bus to the bus stop 6.
Note:
1 <= routes.length <= 500
.1 <= routes[i].length <= 500
.0 <= routes[i][j] < 10 ^ 6
.
思路:
这道题目思路倒不难,就是用BFS。但是稍微实现不好,就容易引起MLE或者TLE。先看看我的血泪史:1)开始的时候在队列中存储{stop_id, route_id, bus_num},表示在stop_id这一站,并且乘坐在route_id上,需要换乘bus_num次公交,但是这样最后一个测试用例引起了MLE,我分析是因为这种组合太多了,导致队列中的内容过多。2)后来我改成在队列中只存储{route_id, bus_num},表示乘客换乘到route_id这条线路上之后,已经经历过的换乘次数,这样的话,我们就需要维护一个visited_routes,记录当前已经坐过的线路,然后每次从队列头部取出一条线路之后,需要检查这条线路上的所有站点是否为T,如果不是,再增加相应的换乘route_id。思路很完美对不对?可惜TLE了。现在看来经过研究返现的两种完美的解决方案:
1)记录已经访问过的站点:每次在队列头部遇到一个站点时,我们就看从该站点可以换乘到哪些线路,并检查这些线路上都有哪些站点。如果某个站点恰好是终点,则返回;否则就将通过它可以换乘到的所有线路上的所有未访问站点都加入队列中。
2)记录已经访问过的线路:每次在队列头部遇到一个线路时,我们就逐个检查这个线路上的各个站点,如果它是终点,则直接返回;否则就依次检查通过该站点还可以到达哪些线路,如果这些线路还没有被访问过,则将这些线路加入队列中。
无论是思路1)还是思路2),我们在每次检查完一个线路后,都可以将这个线路上的站点集合给清空,表示我们后面再也不会考虑坐这个线路了。这样可以将运行时间降低到原来的1/10。
代码:
1)记录站点的版本:
class Solution {
public:
int numBusesToDestination(vector<vector<int>>& routes, int S, int T) {
unordered_map<int, unordered_set<int>> hash; // map from stop to route
for (int i = 0; i < routes.size(); ++i) {
for (auto& j : routes[i]) hash[j].insert(i);
}
queue<pair<int, int>> q;
q.push(make_pair(S, 0));
unordered_set<int> visited = {S}; // the visited stops
while (!q.empty()) {
int stop = q.front().first;
int bus_num = q.front().second;
q.pop();
if (stop == T) {
return bus_num;
}
for (auto& route_i : hash[stop]) { // all the routes that can be transfer at this stop
for (auto& next_stop : routes[route_i]) {
if (visited.find(next_stop) == visited.end()) {
visited.insert(next_stop);
q.push(make_pair(next_stop, bus_num + 1));
}
}
routes[route_i].clear();
}
}
return -1;
}
};
2)记录线路的版本:
class Solution {
public:
int numBusesToDestination(vector<vector<int>>& routes, int S, int T) {
if (S == T) { // trivial case
return 0;
}
unordered_map<int, vector<int>> hash; // map from stop ID to routes ID
for (int r = 0; r < routes.size(); ++r) {
for (int s : routes[r]) {
hash[s].push_back(r);
}
}
queue<vector<int>> q; // {routes ID, bus_num}
for (int r : hash[S]) { // for each route at stop S
q.push({r, 1});
}
while (!q.empty()) {
int route_id = q.front()[0], bus_num = q.front()[1];
q.pop();
for (int s : routes[route_id]) { // try all stops at this route_id
if (s == T) { // T is on this routine
return bus_num;
}
for (int r: hash[s]) { // the route at stop s
q.push({r, bus_num + 1});
}
hash[s].clear();
}
routes[route_id].clear(); // key points for accelleration
}
return -1;
}
};