JOI’95 予選 問題4 解答・解説

問題4 正解 (2点)

      (a) C       current->next_ptr=next
          Pascal  current^.next_ptr:=next
          BASIC   linkedLamp(current).nextPtr=new

      (b) C       previous->lamp_state
          Pascal  previous^.lamp_state
          BASIC   linkedLamp(previous).lampState

      (c) C       current->lamp_state
          Pascal  current^.lamp_state
          BASIC   linkedLamp(current).lampState
      (b),(c)は、両方正解で1点である。
    


 この問題はもともと国際数学オリンピックの問題候補であった。数学の 問題としては難しくてもコンピュータを使ってシミュレートしてみるのは 容易である。円状にならんでいるランプを表わすために環状リストを 用いる。

 環状リストは、図1(省略)のように要素を円状に並べて 矢印(ポインタ)で結んだものである。headは環状リストの頭部を仮に そこに設定することを示している。 各要素は1つのランプを表わすので、「いま点灯しているか 否か」を表わす lamp_state と「自分の次のランプはどれか」を 表わす next_ptr を値として持つ必要がある。 これをPascalではレコード型 lamp_type で、Cでは構造体 lamp_type で、 Quick BASICではユーザ定義のレコード型 lampType で表わす。 これらの要素がどのようにリンクされているかを 表わすためにPascalやCではポインタを用いているが、BASICにはそのような 概念がないので配列 linkedLamp を使って表わしている。 すなわち、図1の環状リストを図2(省略)に示したように配列表現している。

 空欄は手続き(プロシージャ、関数)make_linked_lamp と count_op に合計3箇所ある。
 make_linked_lamp はランプの個数 num_lamp を受け取り、 円状に num_lamp 個のランプを並べて初期化する。 いま current 位置のランプまで初期化が終わったとしよう (current と next はCとPascalではポインタ、BASICでは配列の添字である)。 その次のランプの初期化を行なうためには、next 位置にランプを置き (CとPascalでは malloc 関数や new 手続きで新しい領域を確保する。 BASICでは current の隣の配列要素を使う)、ランプの状態を ON すなわち1 に設定し(これが空欄(a)の直前の行で行なっていることである)、 直前位置のランプからこのランプへのリンクを設定し(この部分が空欄(a)の 中で行なっていることである)、最後に初期化の終わった 最終位置 current を更新する(これが空欄(a)の直後の行で行なって いることである)。最後のランプの初期化が終わったら、 そのランプからへ head 位置のランプへのリンクを設定すれば ランプの円状配置が完了する。
 count_op は、環状リストの上をぐるぐるめぐりながら、 操作規則1、2に従ってランプをオン・オフしてゆく。その際、オンだった ランプの個数 num_on を数え、この値がランプの個数 num_lamp に等しくなる まで繰り返す。空欄(b)では、いま見ているランプのひとつ手前のランプが 点灯しているか否かを判定しており、点灯している場合は ランプ操作規則1を適用するために空欄(c)において、今見ているランプが 消えているか否かを判定して場合分けを行なっている。
 このようなポインタを使ったリスト表現は非常に有用である。 環状リストではない線形リストの使用例とその簡単な解説が 「第4回国際情報オリンピックの2日目の問題解説」と 「本冊子の本選問題2の解説」にもあるので参照されたい。