シミュレーション型の問題の読み方
レジの行列、担当者への仕事の割り当て、来場者数の累計—— 第3問の後半に出てくる長い問題文の問題です。 一見むずかしそうですが、やっていることはここまでの型の組み合わせにすぎません。 怖がらずに表を書き始められるかどうかが勝負です。
APPROACH
長い問題文にどう向き合うか
シミュレーション型は、問題文が長く、身近な題材(お店、学校行事、当番表)で書かれています。 そのため「読解問題」に見えますが、実際に使う情報はごく一部です。 次の3ステップで機械的に処理します。
- 登場する配列と変数を書き出す … Arrive、service、Start のように、何を表すかを1行でメモする
- 1周でやることを1文にする … 「開始時刻を決めて、終了時刻を出す」のように短くまとめる
- 表を作って1行ずつ埋める … あとはトレースと同じ作業
出てくるパターンは4つだけ
| パターン | 中心になる式 | 題材の例 |
|---|---|---|
| 順番待ち | 最大値(到着, 前の終了) | レジ、窓口、順番待ち |
| 割り当て | いちばん小さい担当者を探して足す | 当番、作業分担、係決め |
| 累積で判定 | total を足しながら上限と比べる | 来場者数、定員、予算 |
| 連続の長さ | 続けばカウント、切れたら0に戻す | 連勤、連続記録、空き状況 |
EXERCISE 1
例題1:順番待ちの開始時刻
1つの窓口で順番に処理します。i番目の人の開始時刻 Start[i] を正しく求めるには、[ ア ]に何を入れればよいですか。
Arrive = [0, 2, 4, 9, 11] service = 5 Start = [0, 0, 0, 0, 0] Finish = [0, 0, 0, 0, 0] Start[0] = Arrive[0] Finish[0] = Start[0] + service i を 1 から 要素数(Arrive) - 1 まで 1 ずつ増やしながら繰り返す: |Start[i] = [ ア ] ⎿ Finish[i] = Start[i] + service 表示する(Start)
選択肢:① 最大値(Arrive[i], Finish[i - 1]) ② 最小値(Arrive[i], Finish[i - 1]) ③ Arrive[i] + service ④ Finish[i - 1] - service
答えと解説を見る
答え:① 最大値(Arrive[i], Finish[i - 1])
対応を始めるには2つの条件が同時にそろう必要があります。
- その人が到着していること … Arrive[i] 以降
- 前の人が終わっていること … Finish[i-1] 以降
両方がそろう時刻は、2つのうち遅いほうです。だから最大値を使います。 「待ち行列なのに最大値?」と違和感を持つ人が多いところですが、 「遅いほうに合わせる=最大値」と覚えてください。
| i | 到着 | 前の終了 | 開始 | 終了 | 待ち |
|---|---|---|---|---|---|
| 0 | 0 | — | 0 | 5 | 0 |
| 1 | 2 | 5 | 5 | 10 | 3 |
| 2 | 4 | 10 | 10 | 15 | 6 |
| 3 | 9 | 15 | 15 | 20 | 6 |
| 4 | 11 | 20 | 20 | 25 | 9 |
② の最小値を選ぶと、前の人が終わる前に始めてしまうことになり、現実に合いません。 ③ は到着してすぐ終わることになり、④ は時間が巻き戻ります。
待ち時間 = 開始 − 到着です。引く順番を逆にすると負の数になります。 この処理の動きは待ち行列シミュレーターで1人ずつ確認できます。
EXERCISE 2
例題2:累積が上限を超える時点
次のプログラムを実行したとき、表示される day の値として正しいものを選びなさい。
Visitors = [18, 22, 15, 30, 12, 20] limit = 70 total = 0 day = -1 i を 0 から 要素数(Visitors) - 1 まで 1 ずつ増やしながら繰り返す: |total = total + Visitors[i] |もし total >= limit かつ day == -1 ならば: ⎿⎿ day = i 表示する(day)
選択肢:① 2 ② 3 ③ 4 ④ 5
答えと解説を見る
答え:② 3
これは累積和と、-1 を見張り番に使う テクニックの組み合わせです。新しい考え方は何も出てきません。
| i | 人数 | 累計 | 70以上? | day |
|---|---|---|---|---|
| 0 | 18 | 18 | × | -1 |
| 1 | 22 | 40 | × | -1 |
| 2 | 15 | 55 | × | -1 |
| 3 | 30 | 85 | ○ | 3 |
| 4 | 12 | 97 | ○ | 3 のまま |
| 5 | 20 | 117 | ○ | 3 のまま |
初めて70以上になるのは i = 3 のときです。
day == -1 の条件があるおかげで、それ以降は上書きされません。
もし day == -1 がなければ、最後の i = 5 で上書きされて
答えは 5 になります。④ はその間違いを選ばせる選択肢です。
条件式に「かつ」でつながれた部分があったら、必ず役割を確認してください。
また、答えは「4日目」ではなく添字の 3 です。 問題文が「何日目か」と聞いていれば 4 と答える必要があります。 添字を答えるのか、順番を答えるのかを必ず確認してください。
EXERCISE 3
例題3:連続している区間の長さ
次のプログラムを実行したとき、表示される longest の値として正しいものを選びなさい。
Busy = [1, 1, 0, 1, 1, 1, 0, 1] now = 0 longest = 0 i を 0 から 要素数(Busy) - 1 まで 1 ずつ増やしながら繰り返す: |もし Busy[i] == 1 ならば: ||now = now + 1 ||もし now > longest ならば: |⎿⎿ longest = now |そうでなければ: ⎿⎿ now = 0 表示する(longest)
選択肢:① 2 ② 3 ③ 5 ④ 6
答えと解説を見る
答え:② 3
この形は2つの箱を使い分けるのが要点です。
now… いま続いている長さ。途切れたら 0 に戻すlongest… これまでの最長。0 に戻さない
| i | Busy[i] | now | longest |
|---|---|---|---|
| 0 | 1 | 1 | 1 |
| 1 | 1 | 2 | 2 |
| 2 | 0 | 0 | 2 |
| 3 | 1 | 1 | 2 |
| 4 | 1 | 2 | 2 |
| 5 | 1 | 3 | 3 |
| 6 | 0 | 0 | 3 |
| 7 | 1 | 1 | 3 |
1が続く区間は「2個」「3個」「1個」の3か所で、最長は 3 です。
③ の5は、1の総数(2+3+1 = 6ではなく、途中まで数えた場合)を答えた間違い、 ④ の6は1の個数を全部数えた間違いです。 聞かれているのは「連続している長さ」であって「個数」ではありません。
「そうでなければ now = 0」を見落とすと、ただの個数になってしまいます。 この1行があるかどうかで意味がまったく変わるため、 リセットの行を探すのがこの形を見抜くコツです。
ASSIGN
割り当て問題は「最小値を探して足す」
当番や作業の割り当ては、いちばん空いている人を探して、その人に仕事を足すという繰り返しです。 最小値の探索と集計を組み合わせた形になります。
Days = [4, 1, 3, 1, 3, 4] Available = [1, 1, 1] work を 0 から 要素数(Days) - 1 まで 1 ずつ増やしながら繰り返す: |member = 0 |i を 1 から 要素数(Available) - 1 まで 1 ずつ増やしながら繰り返す: ||もし Available[i] < Available[member] ならば: |⎿⎿ member = i ⎿ Available[member] = Available[member] + Days[work] 表示する(Available)
内側のループが「いちばん小さい人を探す」部分、
最後の1行が「その人に足す」部分です。
member = 0 が外側ループの中にあることに注目してください。
仕事ごとに探し直すため、ここでリセットする必要があります。
| 仕事 | 日数 | 選ばれる人 | Available |
|---|---|---|---|
| 開始 | — | — | [1, 1, 1] |
| 0 | 4 | 0 | [5, 1, 1] |
| 1 | 1 | 1 | [5, 2, 1] |
| 2 | 3 | 2 | [5, 2, 4] |
| 3 | 1 | 1 | [5, 3, 4] |
同じ値が並んだときは、先に見つけたほうが選ばれます。
条件が < なので、同じ値では更新されないためです。
<= にすると後のほうが選ばれ、答えが変わります。ここも問われやすい点です。
EXERCISE 4
例題4:全部そろったら打ち切る条件
座席を前から順にグループへ割り当てます。 すべてのグループが必要数を満たしたら繰り返しを抜けるには、[ ア ]に何を入れればよいですか。
Seats = [2, 4, 1, 3, 2] Need = [3, 5, 2] Filled = [0, 0, 0] group = 0 i を 0 から 要素数(Seats) - 1 まで 1 ずつ増やしながら繰り返す: |Filled[group] = Filled[group] + Seats[i] |もし Filled[group] >= Need[group] ならば: |⎿ group = group + 1 |もし [ ア ] ならば: ⎿⎿ 繰り返しを抜ける 表示する(group)
選択肢:① group == 要素数(Need) ② group == 要素数(Seats) ③ Filled[group] == 0 ④ i == group
答えと解説を見る
答え:① group == 要素数(Need)
まず、変数が何を表しているかを1行ずつメモします。ここを飛ばすと必ず迷います。
Seats… 前から順に来る座席のかたまりNeed… 各グループが必要とする座席数(3人分・5人分・2人分)Filled… 各グループに今まで入った数group… 今どのグループに入れているか。同時に「完了した数」でもある
鍵は group です。
必要数に届くと group = group + 1 で次へ進むので、
この値は「終わったグループの数」と同じになります。
グループは全部で 要素数(Need) 個なので、
group がその値になった=全部終わったということです。
| i | Seats[i] | 入れる先 | Filled | group |
|---|---|---|---|---|
| 開始前 | — | — | [0, 0, 0] | 0 |
| 0 | 2 | group 0 | [2, 0, 0] | 0 |
| 1 | 4 | group 0 | [6, 0, 0] | 1(3以上で完了) |
| 2 | 1 | group 1 | [6, 1, 0] | 1 |
| 3 | 3 | group 1 | [6, 4, 0] | 1 |
| 4 | 2 | group 1 | [6, 6, 0] | 2(5以上で完了) |
この例では Seats を使い切っても group は 2 までで、 3つ目のグループは完了しません。表示されるのは 2 です。 つまり「抜ける」条件は一度も成立しない——それでもプログラムとしては正しい、という点も確認しておいてください。
- ② group == 要素数(Seats) … 比べる相手が違います。 Seats は座席のかたまりの個数(5)で、グループ数ではありません。 2つの配列が出てきたとき、どちらの個数と比べるのかが最大の分かれ目です
- ③ Filled[group] == 0 … 開始直後に成り立ってしまい、1回目で抜けてしまいます
- ④ i == group … i は座席の番号、group はグループ番号で、比べる意味がありません
EXERCISE 5
例題5:前の日と比べる
前の値より大きくなった回数を数えるには、[ ア ]に何を入れればよいですか。
Temps = [18, 20, 19, 22, 22, 25] count = 0 i を 1 から 要素数(Temps) - 1 まで 1 ずつ増やしながら繰り返す: |もし [ ア ] ならば: ⎿⎿ count = count + 1 表示する(count)
選択肢:① Temps[i] > Temps[i - 1] ② Temps[i] < Temps[i - 1] ③ Temps[i] == Temps[i - 1] ④ Temps[i] >= Temps[i - 1]
答えと解説を見る
答え:① Temps[i] > Temps[i - 1]
日ごとのデータを扱う問題では、「前の日」と比べる形が非常によく出ます。 まず注目したいのは、繰り返しが 0 ではなく 1 から始まっている点です。
1日目には「前の日」が存在しないからです。
もし 0 から始めると Temps[-1] という存在しない場所を見ることになります。
「− 1 を使うなら 1 から始める」——
繰り返しの回数で扱った「+ 1 を使うなら1つ手前まで」とちょうど対になる形です。
| i | 前日 | 当日 | 増えた? | count |
|---|---|---|---|---|
| 1 | 18 | 20 | ○ | 1 |
| 2 | 20 | 19 | × | 1 |
| 3 | 19 | 22 | ○ | 2 |
| 4 | 22 | 22 | ×(同じ) | 2 |
| 5 | 22 | 25 | ○ | 3 |
答えは 3 です。 i = 4 で 22 と 22 が並ぶように、データがわざわざ作られている点に注目してください。 同じ値のときにどうするかを試すための仕掛けです。
- ④ >= を選ぶと 4 になります。「増えた」に「変わらなかった」を含めてしまうためです
- ② < … 下がった回数(1回)になります
- ③ == … 同じだった回数(1回)になります
SUMMARY
型の組み合わせで読む
ここまで見たとおり、シミュレーション型に新しい文法は出てきません。 使われているのは、これまでの5テーマの型だけです。
| 問題の種類 | 使われている型 |
|---|---|
| 順番待ち | 配列の読み方 + 前の要素の参照(i - 1) |
| 累積で判定 | 集計(累積和)+ -1 を見張り番に使う |
| 連続の長さ | 集計(カウント)+ 最大値の更新 + リセット |
| 割り当て | 最小値の探索 + 集計 + 二重ループ |
問題文が長くても、コードの中で使われている型を1つずつ見分けていけば読み解けます。 逆にいえば、ここまでの5テーマが身についていれば、発展問題も特別な準備は要りません。
CHECK
このテーマのチェックリスト
- 長い問題文でも、まず配列と変数を書き出す手順が身についている
- 順番待ちの開始時刻が最大値で求まる理由を説明できる
- 待ち時間が開始 − 到着だと分かる
- 累積の判定で
day == -1が上書きを防いでいると分かる - 連続の長さで
now = 0のリセットを探すべきだと分かる - 答えるのが添字か順番かを最後に確認する習慣がある
- 打ち切り条件をゴールの日本語から式に直せる
- 前日と比べる処理は i を 1 から始めると分かる
6テーマすべてを読み終えたら、あとは演習です。 クイズの「発展」で210問のうちの発展30問に挑戦してみてください。