基本情報技術者 2012年 秋期 午後 問08
次のプログラムの説明及びプログラムを読んで、設問に答えよ。
鉄道の路線がある。駅数Nは2以上で、各駅には駅番号(1, 2, …, N)が付いている。どの任意の2駅間にも、それらを結ぶ経路が一つ以上存在する。また、隣接する2駅間を直接結ぶ経路は一つだけ存在し、その距離が与えられている。図1に5駅からなる鉄道の路線例を示す。
〔プログラムの説明〕
副プログラムCalcDistは、駅数N及び要素数N×Nの2次元配列Distを受け取り、プログラムに示すアルゴリズム(Warshall-Floyd法)によって、配列Distの内容を更新しながら各駅間の最短距離を求めていく。実行が終わると、任意の2駅i, j間の最短距離がDist[i][j]に求められている。どの2駅についても、その最短距離は999より小さいものとする。配列Distの添字は1から始まる。
副プログラムCalcDistに渡す配列Distには、次の値を格納する。なお、図1の路線情報を格納した配列Distの内容を、図2の初期値に示してある。
(1) 2駅i, jが隣接しているとき、Dist[i][j]及びDist[j][i]には、その駅間距離を格納する。例えば、図1の駅①と②は隣接しているので、Dist[1][2]及びDist[2][1]には、18を格納する。
(2) 2駅i, jが隣接していないとき、Dist[i][j]及びDist[j][i]には、未確定を表す距離999を格納する。例えば、図1の駅①と③は隣接していないので、Dist[1][3]及びDist[3][1]には、999を格納する。
(3) 各駅iについて、Dist[i][i]には0を格納する。
副プログラムCalcDist中のPrint(N, Dist)は、配列Distのその時点の内容を印字する副プログラムである。
図1の路線情報を配列Distに格納して、CalcDistを実行した。配列Distの内容の変化をPrint(N, Dist)で印字した結果は、図2のとおりであった。
駅数Nに関して、副プログラムCalcDistの計算量のオーダはc、またメモリ使用量のオーダはdである。そこで、駅数Nが大きい場合に、計算量やメモリ使用量を減らすための方法を考える。
例えば、配列Distの対称性に着目し、プログラム中の破線で囲んだ部分を、

とすれば、繰返し処理中の選択処理の実行回数を約半分に減らすことができる。
次に、少ないメモリ使用量で、任意の2駅i, j間の最短距離を求める方法を考える。
駅を、乗換駅(3方向以上に隣接する駅がある)、終端駅(1方向だけに隣接する駅がある)、中間駅(2方向だけに隣接する駅がある)の3種類に分ける。乗換駅と終端駅を合わせて基幹駅と呼ぶ。基幹駅の数をKとして、駅番号1~Kが基幹駅、駅番号K+1~Nが中間駅となるように駅番号を定める。ここで、K≧2であるとする。図3の路線例では、駅①~③が乗換駅、駅④が終端駅、駅⑤~⑩が中間駅である。


各駅間の最短距離は、次の方法で求める。
(1) 中間駅を全て除いて、基幹駅だけからなる路線を考え、二つの基幹駅を結ぶ経路ごとに一意の区間名を付ける。図3の路線例にこの操作を加えたものが図4である。例えば、図3の経路①-⑤-⑥-③は、図4の区間Bに対応する。
(2) 要素数K×Kの2次元配列Distを用意し、事前に配列Distに各基幹駅間の最短距離を求めておく。
(3) 全ての駅について、表1に示す形式の駅情報表を用意する(表1には、図3の駅情報の一部が示してある)。
中間駅の場合、Secはその駅が属する区間の区間名、KL, KHはその駅が属する区間の両端の駅番号(KL≦KHとする)、ToKL, ToKHはその駅から駅KL, KHまでの距離である。基幹駅の場合、その駅は自分自身の駅を両端とする距離0の区間に属すると考えて、表1の例のように中間駅と同様の情報を用意する。
これらの情報は、駅番号を添字として参照する。例えば、駅番号5のToKLの値はToKL[5]として参照する。

(4) 2駅i, j間の最短距離を求める。一般に、両駅が属する区間が異なる場合、駅iから駅jへ行く経路は、
(駅i)→(駅iが属する区間のいずれか一端の基幹駅)
→(駅jが属する区間のいずれか一端の基幹駅)→(駅j)
となる。したがって、次の式で求める4通りの値D1, D2, D3, D4のうちの最小値が、2駅i, j間の最短距離となる。
・D1 ← ToKL[i] + Dist[KL[i]][KL[j]] + ToKL[j]
・D2 ← ToKL[i] + Dist[KL[i]][KH[j]] + ToKH[j]
・D3 ← ToKH[i] + Dist[KH[i]][KL[j]] + ToKL[j]
・D4 ← ToKH[i] + Dist[KH[i]][KH[j]] + ToKH[j]
両駅が属する区間が同じ場合は、区間の端の基幹駅を経由せずに直接駅iから駅jへ行く経路がある。これも考慮すると、任意の2駅i, j間の最短距離Resは次の処理で求められる。ここで、関数min(式1, 式2, …)は、式1, 式2, …の値の最小値を返す関数であり、関数abs(式)は、式の値の絶対値を返す関数である。

この方法なら、2次元配列Distの要素数をN×NからK×Kに削減できる。ただし、表1に示す表が増えるので、例えばK=5のとき、配列及び表が占めるメモリ使用量を削減できるのはN≧gの場合となる。ここで、表1に示す表は、1駅について配列Distの要素5個分のメモリを使用するものとする。
設問1:
問題文を見る 本文中及び図2中のに入れる正しい答えを、解答群の中から選べ。
a, bに関する解答群
ア:33
イ:34
ウ:35
エ:999
c, dに関する解答群
ア:
イ:
ウ:
エ:
eに関する解答群
ア:1, To ≦ From, 1
イ:1, To ≦ From + 1, 1
ウ:From, To ≦ N - 1, 1
エ:From + 1, To ≦ N, 1
fに関する解答群
ア:(KL[i] = KL[j]) and (KH[i] = KH[j])
イ:(KL[i] ≠ KL[j]) or (KH[i] ≠ KH[j])
ウ:Sec[i] = Sec[j]
エ:Sec[i] ≠ Sec[j]
gに関する解答群
ア:6
イ:8
ウ:9
エ:14
模範解答
a:ウ、b:ア、c:エ、d:ウ、e:エ、f:ウ、g:ウ
解説
解答の導き方
a、b:Via = 2のときの距離
プログラムは、駅 を経由すると距離を短くできるかを調べます。条件は、現在の距離 と、 から を通って へ行く距離を比べています。
図2のVia = 2では、駅1から駅3へ駅2を経由する距離は、駅1から駅2までの18と、駅2から駅3までの17を足して、 です。元の距離は999なので、35に更新されます。したがって、aには35が入ります。
駅3から駅5へも、駅2を経由できます。駅3から駅2までの17と、駅2から駅5までの16を足すと、 です。元の距離999より短いので、bには33が入ります。
c、d:計算量とメモリ使用量
計算量のオーダは、駅数 が大きくなったときに、処理回数がどの程度の割合で増えるかを表します。
プログラムでは、、、 の3つの繰返しがあり、それぞれが駅数 に応じて実行されます。そのため、距離を比較する処理はおよそ 回です。印字にも配列の要素数に応じた処理が必要ですが、全体のオーダは変わりません。よって、cは です。
一方、配列 は 個の要素を持ちます。したがって、必要なメモリ量は に比例し、dは です。
e:対称性を使って繰返しを減らす
駅間の距離は、駅 から駅 までと、駅 から駅 までで同じです。つまり、配列 は対称です。
そのため、駅の組み合わせを両方向について調べる必要はありません。例えば、駅1と駅3の組み合わせを調べるときに、駅3と駅1の組み合わせも別に調べるのではなく、一度だけ調べます。更新時には、 と の両方に同じ値を入れます。
このとき、 より大きい駅番号だけを に選べば、駅の組み合わせを一度ずつ調べられます。したがって、Toの範囲は「From + 1, To ≦ N, 1」です。
f:同じ区間に属する駅の扱い
異なる区間に属する駅どうしは、駅 から区間の端の基幹駅へ進み、基幹駅どうしを移動してから、駅 へ向かう必要があります。この場合は、端の選び方に応じた から の最小値を使います。
同じ区間に属していれば、区間の端の基幹駅を通らずに、区間内を直接移動する経路もあります。駅情報表では、同じ区間かどうかを と の値で判定できます。したがって、直接移動する距離も候補に加える条件は「」です。直接移動の距離は、同じ端の駅からの距離の差の絶対値で求められます。
g:メモリ使用量の比較
元の方法では、配列 の要素数は です。
新しい方法では、基幹駅間の配列が 個で、駅情報表は1駅につき配列 の要素5個分です。したがって、新しい方法のメモリ使用量は、配列 の要素数に換算すると です。
のとき、新しい方法は 個分です。元の方法よりメモリ使用量を減らすには、次の不等式を満たす必要があります。
境目を確認すると、 では元の方法が64個分、新しい方法が65個分なので、まだ減りません。 では元の方法が81個分、新しい方法が70個分となり、新しい方法の方が少なくなります。よって、最小の駅数は9です。
誤りやすいポイント
- Via = 2の距離更新では、経由する駅2までの距離と、駅2から目的駅までの距離を足します。例えば駅1から駅3なら、 です。
- 条件の判定回数も計算量に含めます。距離を実際に更新する回数が少なくても、3重ループの比較回数は です。
- 対称性を使う場合、片側だけを更新すると配列の対称性が崩れます。 と の両方を更新します。
- 同じ区間かどうかは、端の駅番号だけでなく区間名 で判定します。
- メモリの比較では、新しい配列だけでなく、駅情報表の 個分も加えて考えます。
FAQ
Q: 999は実際の駅間距離として使う値ですか?
A: いいえ。隣接していない駅どうしの距離が未確定であることを表す値です。この問題では、どの2駅間の最短距離も999より小さいとされています。
A: いいえ。隣接していない駅どうしの距離が未確定であることを表す値です。この問題では、どの2駅間の最短距離も999より小さいとされています。
Q: 同じ区間の駅どうしでも、基幹駅を経由する経路を調べるのはなぜですか?
A: 区間内を直接移動する経路が最短とは限らないためです。直接移動する距離と、基幹駅を経由する から の距離を比べ、最も短いものを選びます。
A: 区間内を直接移動する経路が最短とは限らないためです。直接移動する距離と、基幹駅を経由する から の距離を比べ、最も短いものを選びます。
Q: の配列で、駅数 の駅どうしの距離を扱えるのはなぜですか?
A: 配列には基幹駅どうしの最短距離を保存し、中間駅については駅情報表に区間と両端までの距離を保存します。中間駅を含む距離は、その情報と基幹駅間の距離を組み合わせて求めます。
A: 配列には基幹駅どうしの最短距離を保存し、中間駅については駅情報表に区間と両端までの距離を保存します。中間駅を含む距離は、その情報と基幹駅間の距離を組み合わせて求めます。
関連キーワード: Warshall-Floyd法、最短経路問題、計算量、空間計算量、対称行列




