C++で解くバス路線問題:目的地までの最小乗車回数を求めるアルゴリズム
問題概要
バス路線のリストが与えられます。各 routes[i] には、i 番目のバスが永遠に繰り返し走行する経路が格納されています。たとえば routes[0] = [1, 5, 7] の場合、0 番目のバスは「1 → 5 → 7 → 1 → 5 → 7 → …」という順序で無限に巡回し続けます。
ここで、バス停 S から出発し(最初はまだどのバスにも乗っていない状態)、バス停 T へ向かうことを考えます。目的地に到達するためには、最低何台のバスに乗る必要があるでしょうか。到達が不可能な場合は -1 を返してください。
たとえば、入力が [[1,2,8],[3,6,8]]、S = 1、T = 6 のとき、出力は 2 になります。これは、まず 1 台目のバスで乗り換え地点となるバス停 8 まで移動し、そこで 2 台目のバスに乗り換えてバス停 6 へ向かうためです。
解法のアプローチ
この問題は幅優先探索(BFS)を使うことで効率的に解けます。ポイントは、「バス停」ではなく「路線」単位で探索することです。同じ路線に乗り続けている間は乗車回数が増えないため、訪問済みの路線を管理しながら、乗車回数(レベル)ごとに探索範囲を広げていきます。
具体的な手順は以下の通りです。
- マップ
mを定義する i := 0として、i < r.size()の間iを 1 ずつ増やしながら以下を繰り返すj := 0として、j < r[i].size()の間jを 1 ずつ増やしながら以下を繰り返すm[r[i][j]]の末尾にi(路線番号)を追加する
- キュー
qを定義し、Sを追加する S == Tの場合は0を返す- 集合
visitedを定義する lvl := 1として、qが空でない間lvlを 1 ずつ増やしながら以下を繰り返すsz := q.size()szが 0 でない間、以下を繰り返すnode := q.front()で先頭要素を取り出し、キューから削除するi := 0として、i < m[node].size()の間iを 1 ずつ増やしながら以下を繰り返すroute := m[node][i]routeがすでにvisitedに含まれている場合は、処理をスキップして次へ進むrouteをvisitedに追加するj := 0として、j < r[route].size()の間jを 1 ずつ増やしながら以下を繰り返すstop := r[route][j]stop == Tの場合はlvlを返すstopをキューqに追加する
szを 1 減らす
- 最後に
-1を返す
それでは、理解を深めるために以下の実装例を見てみましょう。
C++ 実装例
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int numBusesToDestination(vector<vector<int>>& r, int S, int T) {
unordered_map<int, vector<int>> m;
for(int i = 0; i < r.size(); i++){
for(int j = 0; j < r[i].size(); j++){
m[r[i][j]].push_back(i);
}
}
queue<int> q;
q.push(S);
if(S == T) return 0;
unordered_set<int> visited;
for(int lvl = 1; !q.empty(); lvl++){
int sz = q.size();
while(sz--){
int node = q.front();
q.pop();
for(int i = 0; i < m[node].size(); i++){
int route = m[node][i];
if(visited.count(route)) continue;
visited.insert(route);
for(int j = 0; j < r[route].size(); j++){
int stop = r[route][j];
if(stop == T) return lvl;
q.push(stop);
}
}
}
}
return -1;
}
};
main(){
Solution ob;
vector<vector<int>> v = {{1,2,8}, {3,6,8}};
cout << (ob.numBusesToDestination(v, 1, 6));
}入力
{{1,2,8}, {3,6,8}}
1
6出力
2
補足:計算量について
前処理で全バス停と路線の対応関係を構築するのに O(総バス停数)、BFS 本体では各路線を高々一度しか訪問しないため、全体の計算量は O(路線数 × 1路線あたりのバス停数) 程度に抑えられます。「乗車回数の最小化」という問題をグラフ探索に帰着させる、典型的な BFS の応用例といえるでしょう。
-
C++で解くリスのナッツ収集シミュレーション ― 最小移動距離を求めるアルゴリズム
問題概要 1本の木、1匹のリス、そして複数のナッツがフィールド上にあります。それぞれの位置は2次元グリッドのセルで表現されます。この問題の目的は、リスがすべてのナッツを集めて木の下に1個ずつ運ぶときの最小移動距離を求めることです。 リスの行動には次の制約があります。 一度に持てるナッツは最大1個 移動は上下左右の4方向で、隣接するセルへのみ可能 距離は移動回数(ステップ数)で表される たとえば、入力が「高さ: 5 / 幅: 7 / 木の位置: [2,2] / リスの位置: [4,4] / ナッツ: [[3,0], [2,5]]」の場合、出力は 12 となります。 解法のポイント まず、
-
C++で解く長方形エリアII ― 座標圧縮と走査線法による被覆面積の計算
問題概要 軸に平行な長方形のリストが与えられるものとします。各 rectangle[i] = {x1, y1, x2, y2} において、(x1, y1) は i 番目の長方形の左下隅の座標、(x2, y2) は右上隅の座標を表します。 求めたいのは、平面上でこれらすべての長方形が覆っている領域の合計面積です。答えは非常に大きな値になる可能性があるため、109 + 7 で割った余りを返すことになっています。 たとえば、入力が次のような場合を考えてみましょう。 このとき、出力は 6 となります。 解法の方針:座標圧縮+走査線(スイープライン) この問題は、座標圧縮(座標の離散化)と走査線法(