基本情報技術者 2017年 春期 午後 問08
次のプログラムの説明及びプログラムを読んで、設問1,2に答えよ。
副プログラムShortestPathは、N個(N > 1)の地点と、地点間を直接結ぶ経路及び距離が与えられたとき、出発地から目的地に至る最短経路とその距離を求めるプログラムである。最短経路とは、ある地点から別の地点へ最短距離で移動する際の経由地を結んだ経路である。副プログラムShortestPathでは、出発地の隣接地点から開始して、目的地に向かって最短距離を順次確定する。ある地点の隣接地点とは、その地点から他の地点を経由せずに直接移動できる地点のことである。
図1は、地点数Nが7の経路の例で、経路をグラフで表現したものである。図1において、丸は地点を示し、各地点には0から始まる番号(以下、地点番号という)が順番に割り当てられている。線分は地点間を直接結ぶ経路を示し、線分の上に示す数字はその距離を表す。また、経路上は、双方向に移動できる。
〔プログラムの説明〕
(1) 副プログラムShortestPathの引数の仕様を、表1に示す。ここで、出発地から目的地までを結ぶ経路は、少なくとも一つ存在するものとする。また、配列の要素番号は、0から始まる。

(2) Distance[i][j](i = 0, …, nPoint − 1; j = 0, …, nPoint − 1)には、地点iから地点jまでの距離が格納されている。ただし、地点iと地点jが同一の地点の場合は0、地点iが地点jの隣接地点ではない場合は−1が格納されている。図1の例における配列Distanceの内容は、表2のとおりである。

(3) 行番号5〜10では、変数、配列に初期値を格納する。
① 最短距離を返却する変数sDistに初期値として∞(最大値を表す定数)を格納し、出発地から目的地までの最短距離が設定されていないことを示す。
② 最短経路を返却する要素数がnPointである配列sRouteの全ての要素に初期値として−1を格納し、最短経路上の地点の地点番号が設定されていないことを示す。
③ 出発地から各地点までの最短距離を設定する配列をpDistとする。pDistは1次元配列であり、要素数はnPointである。配列pDistの全ての要素に初期値として∞を格納する。
④ 出発地から各地点までの最短距離が確定しているかどうかを識別するための配列をpFixedとする。pFixedは1次元配列であり、要素数はnPointである。配列pFixedの全ての要素に初期値としてfalseを格納し、最短距離が未確定であることを示す。最短距離が確定したときにtrueを設定する。例えば、出発地から地点iまでの最短距離が確定したとき、pFixed[i]はtrueとなり、その最短距離はpDist[i]に設定されている。pFixed[i]がfalseの場合は、地点iまでの最短距離は未確定であり、pDist[i]の値は最短距離として確定されていない。
(4) 行番号11では、出発地から出発地自体への最短距離pDist[sp]に0を設定する。
(5) 行番号12〜39の最短経路探索処理では、出発地から各地点までの最短距離を算出しながら、最短経路を求める。
① 行番号13〜22
配列pFixedを調べ、出発地から全ての地点までの最短距離が確定していれば、最短経路探索処理を抜けて(6)に進む。
② 行番号23〜29
出発地からの最短距離が未確定の地点の中で、出発地からの距離が最も短い地点を探し、その地点をsPointとし、その地点の最短距離を確定する。
③ 行番号30〜38
各地点に対して(ア)、(イ)を実行し、①に戻る。
(ア) 地点sPointの隣接地点であり、かつ、出発地からの最短距離が未確定であるかどうかを調べる。
(イ) (ア)の条件を満たす地点jに関して、出発地から地点sPointを経由して地点jに到達する経路の距離を求め、その距離が既に算出してあるpDist[j]よりも短ければ、pDist[j]及びpRoute[j]を更新する。ここで、pDist[j]は、出発地から地点jまでの仮の最短距離となる。pRoute[j]には、そのときの、地点jの直前の経由地の地点番号を設定する。
(6) 行番号40〜48では、出発地から目的地までの最短距離をsDistに、最短経路上の地点の地点番号を目的地から出発地までの順に配列sRouteに設定する。

設問1:
問題文を見る プログラム中のに入れる正しい答えを、解答群の中から選べ。
aに関する解答群
ア:not(pFixed[i])
イ:not(pFixed[j])
ウ:pFixed[i]
エ:pFixed[j]
bに関する解答群
ア:dp
イ:nPoint
ウ:nPoint − 1
エ:sp
オ:sPoint
c, dに関する解答群
ア:pRoute[dp]
イ:pRoute[i]
ウ:pRoute[j]
エ:pRoute[sp]
オ:sRoute[dp]
カ:sRoute[i]
キ:sRoute[j]
ク:sRoute[sp]
模範解答
a:イ、b:オ、c:キ、d:イ
解説
解答の導き方
この問題では、最短距離を確定する地点の選び方と、確定した情報を使って経路をたどる方法を考えます。
1. a:最短距離を比べる相手の条件
行番号14〜18では、 がfalseの地点を探しています。つまり、行番号23に進んだ時点で、 は最短距離がまだ確定していない地点です。
行番号23では、 より後の地点を順に調べます。調べる地点 も未確定であり、さらに が現在の候補 より小さい場合に、候補を に替える必要があります。
したがって、行番号24の条件は「 の最短距離が未確定である」ことを表すnot(pFixed[j]) と、「 の距離の方が短い」ことを表すpDist[j] < pDist[i] の組合せです。aには イ が入ります。
2. b:最短距離が確定した地点
行番号28では、選んだ地点をsPointに保存しています。行番号29では、その地点までの最短距離が確定したことを記録します。
問題文には、最短距離が確定した地点について、配列 pFixed の該当要素を true にするとあります。確定する地点は sPoint なので、pFixed[sPoint] ← true となります。bには オ が入ります。
3. c、d:目的地から出発地へ経路をたどる
行番号35では、地点 までの仮の最短距離を更新するとき、直前の経由地をpRoute[j] に記録しています。つまり、pRoute[ある地点] を見ると、その地点の一つ前に通る地点が分かります。
一方、表1では、出力するsRoute[] の地点番号は「目的地から出発地までの順」とされています。そのため、行番号42で を目的地dpに設定し、行番号43〜47では次の順に処理します。
- 現在の地点 をsRoute[j] に記録する。
- pRoute[i] を使って、現在の地点の一つ前の地点へ移る。
- 出力先の位置 を一つ進める。
出発地に着いた後は、行番号48で出発地spをsRoute[j] に記録します。よって、cには キ、dには イ が入ります。
図1の経路で、例として出発地を0、目的地を6とします。最短経路は、0→1→4→2→5→6です。距離は です。このとき、直前の地点を記録したpRouteをたどると、6から5、2、4、1、0の順に戻れます。
このように、sRoute[]には目的地から出発地へ向かう順で地点番号が格納されます。
誤りやすいポイント
-
iも未確定かどうか調べると考える
行番号14〜18ですでに未確定の地点を に設定しています。行番号24で確認するのは、調べている地点 が未確定かどうかです。 -
pFixedを設定する地点を取り違える
行番号28で、選んだ地点の番号をsPointに保存しています。したがって、確定状態を設定する配列要素はpFixed[sPoint] です。 -
経路を出発地からたどると考える
pRouteには直前の地点が記録されています。そのため、目的地から前の地点をたどると、経路を逆向きに復元できます。これは、sRoute[]の格納順とも一致します。 -
pRoute[i]とsRoute[j]を混同する
は現在の地点番号、 は出力配列の位置です。現在の地点を出力する先はsRoute[j] で、次の地点へ移るために参照するのはpRoute[i] です。
FAQ
Q: pDistとDistanceは、どちらも距離を表しているのですか?
A: 表す内容が異なります。Distance[i][j]は、地点 から地点 へ直接移動する距離です。pDist[j]は、出発地から地点 までの経路全体について、その時点で分かっている距離です。
A: 表す内容が異なります。Distance[i][j]は、地点 から地点 へ直接移動する距離です。pDist[j]は、出発地から地点 までの経路全体について、その時点で分かっている距離です。
Q: なぜ最短経路を目的地からたどるのですか?
A: pRouteには「その地点へ来る直前にいた地点」が記録されているためです。目的地から始めると、直前の地点を次々にたどって出発地まで戻れます。また、出力配列も目的地から出発地までの順に設定する仕様です。
A: pRouteには「その地点へ来る直前にいた地点」が記録されているためです。目的地から始めると、直前の地点を次々にたどって出発地まで戻れます。また、出力配列も目的地から出発地までの順に設定する仕様です。
Q: pRouteの値は、いつ設定されますか?
A: 行番号34〜35で、より短い経路が見つかったときに設定されます。pDist[j]を更新するのと同時に、その経路で地点 に来る直前の地点をpRoute[j] に記録します。
A: 行番号34〜35で、より短い経路が見つかったときに設定されます。pDist[j]を更新するのと同時に、その経路で地点 に来る直前の地点をpRoute[j] に記録します。
関連キーワード: ダイクストラ法、最短経路問題、隣接行列、経路復元
設問2:
問題文を見る 図1において、出発地の地点番号spの値が0、目的地の地点番号dpの値が6の場合について、プログラムの動きを追跡する。行番号12〜39の最短経路探索処理の繰返しで、行番号28のαにおいてsPointに代入された値は、繰返しの1回目は0、2回目は1、3回目はeとなる。また、行番号30〜38の処理が終了した直後のβにおける配列pDistと配列pRouteの値を、表3に示す。最短経路探索処理の繰返しが3回目のときのβにおける配列pDistの値はfとなり、配列pRouteの値はgとなる。ここで、配列pRouteの全ての要素には初期値として0が格納されているものとする。

eに関する解答群
ア:2
イ:3
ウ:4
エ:5
オ:6
fに関する解答群
ア:0,2,8,4,5,10,14
イ:0,2,8,4,5,10,∞
ウ:0,2,8,4,5,11,14
エ:0,2,8,4,5,11,∞
オ:0,2,8,4,5,12,14
カ:0,2,8,4,5,12,∞
gに関する解答群
ア:0,0,0,0,1,3,0
イ:0,0,0,0,1,3,5
ウ:0,0,0,0,2,2,0
エ:0,0,0,0,2,2,5
オ:0,0,4,0,1,2,0
カ:0,0,4,0,2,2,5
模範解答
e:イ、f:カ、g:ア
解説
解答の導き方
このプログラムは、出発地から近い地点を順番に選び、その地点までの最短距離を確定していきます。これはダイクストラ法(出発地から近い地点の最短距離を順に確定する方法)の考え方です。
配列pDistには、出発地から各地点までの「現時点での最短距離」が入ります。まだ距離が分からない地点は∞です。また、pFixedの値がtrueなら、その地点までの最短距離は確定済みです。
1回目の繰返し
出発地は地点0なので、行番号11の処理後、pDist[0]は0です。他の地点のpDistは∞です。
最初に選ばれる地点は、最短距離が未確定の地点のうちpDistが最も小さい地点です。このときは地点0が選ばれ、sPointに0が代入されます。
地点0から直接行ける地点を調べます。図1とDistanceの値から、地点0から地点1までは距離2、地点2までは距離8、地点3までは距離4です。そのため、βでは次の値になります。
これは表3の1回目と一致します。
2回目の繰返し
最短距離が未確定の地点のうち、pDistが最も小さいのは地点1の2です。そのため、sPointに1が代入されます。
地点1から直接行ける未確定の地点は地点4です。出発地から地点1までの距離2に、地点1から地点4までの距離3を足すと、地点4までの距離はです。もともとのpDist[4]は∞なので、pDist[4]を5にし、直前の地点を表すpRoute[4]を1にします。
これも表3の2回目と一致します。
3回目の繰返し
地点0と地点1はすでに確定しています。まだ確定していない地点のpDistは、地点2が8、地点3が4、地点4が5、地点5と地点6が∞です。
この中で最も小さい値は4なので、選ばれるのは地点3です。したがって、行番号28でsPointに代入される値は3です。
次に、地点3から直接行ける地点を調べます。地点0はすでに確定済みなので、更新の対象にはなりません。地点5は未確定で、地点3から地点5までの距離は8です。地点3までの距離4を使って計算すると、地点5までの距離はです。これまでのpDist[5]は∞なので、pDist[5]を12にし、直前の地点を表すpRoute[5]を3にします。
地点2、地点4、地点6については、地点3から直接行けないため更新されません。よって、3回目のβでは次の値になります。
pRouteの初期値は全要素が0です。3回目までに更新されるのは、2回目に更新されたpRoute[4]と、3回目に更新されたpRoute[5]です。したがって、表3の選択肢では、pDistがカ、pRouteがアに該当します。
誤りやすいポイント
- 地点番号の小さい順に選ぶのではありません。 pFixedがfalseの地点の中から、pDistが最も小さい地点を選びます。3回目は地点2の8ではなく、距離が4の地点3を選びます。
- 隣接していない地点へは距離を足しません。 Distance[sPoint][j]が正の値である地点だけが、直接行ける地点として更新の対象になります。
- pRouteの0を、常に「直前の地点が地点0」と判断しないようにします。 この設問ではpRouteの全要素が0から始まるため、まだ更新されていない要素も0のままです。地点5の3回目の値は、地点3からの経路で更新された結果です。
- 目的地が地点6でも、3回目の選択で地点6を選ぶとは限りません。 行番号12〜39では未確定の地点を順に調べるため、その時点で最も距離が短い地点を選びます。
FAQ
Q: pDistの値は、いつ最短距離として確定しますか?
A: その地点が選ばれ、pFixedの値がtrueになったときです。それまでは、pDistはより短い経路が見つかれば更新される仮の距離です。
A: その地点が選ばれ、pFixedの値がtrueになったときです。それまでは、pDistはより短い経路が見つかれば更新される仮の距離です。
Q: 3回目のpRoute[6]が0なのは、地点6の直前が地点0という意味ですか?
A: いいえ。この時点では地点6までの経路が見つかっておらず、pRoute[6]は初期値の0のままです。pRouteの値を直前の地点として読めるのは、経路が見つかって更新された後です。
A: いいえ。この時点では地点6までの経路が見つかっておらず、pRoute[6]は初期値の0のままです。pRouteの値を直前の地点として読めるのは、経路が見つかって更新された後です。
Q: 地点3から地点0へ戻る経路は、更新に使わないのですか?
A: 地点0は1回目に最短距離が確定し、pFixed[0]がtrueになっています。行番号31では未確定の地点だけを更新するため、地点0は対象になりません。
A: 地点0は1回目に最短距離が確定し、pFixed[0]がtrueになっています。行番号31では未確定の地点だけを更新するため、地点0は対象になりません。
関連キーワード: ダイクストラ法、最短経路、隣接行列、貪欲法、経路復元




