競技プログラミング練習会2015 Normal 第2回

28
2015/04/17 長嶺英朗(ID:hnagamin) 競技プログラミング練習会 2015 Normal 2

Transcript of 競技プログラミング練習会2015 Normal 第2回

Page 1: 競技プログラミング練習会2015 Normal 第2回

2015/04/17長嶺英朗(ID:hnagamin)

競技プログラミング練習会2015 Normal

第2回

Page 2: 競技プログラミング練習会2015 Normal 第2回

目次

● スタックとキュー● 全探索

– 幅優先探索– 深さ優先探索

● 累積和

Page 3: 競技プログラミング練習会2015 Normal 第2回

データ構造

● データ構造は多数のデータをある秩序に従って保存しておくための構造です– 例) 配列、ハッシュテーブル、木、スタック、キュー…

● データ構造には様々な種類があり、それぞれ得意なことと不得意なこと、可能なことと不可能なことがあります

● 今日はスタックとキューの話をします

Page 4: 競技プログラミング練習会2015 Normal 第2回

スタック

● 「後入れ先出し」 (Last In, First Out)のデータ構造

● 次のことがO(1)でできる● push: 先頭に要素を追加する● pop: 先頭の要素を取り出す

● 本の山みたいなイメージ

push

pop

Page 5: 競技プログラミング練習会2015 Normal 第2回

キュー

● 「先入れ先出し」 (First In, First Out)のデータ構造

● 次のことがO(1)でできる● enqueue: 先頭に要素を追加する● dequeue: 末尾の要素を取り出す

● 店の行列的なイメージ

enqueue

dequeue

Page 6: 競技プログラミング練習会2015 Normal 第2回

スタックを使う問題の例http://poj.org/problem?id=2559

Page 7: 競技プログラミング練習会2015 Normal 第2回

全探索

Page 8: 競技プログラミング練習会2015 Normal 第2回

全探索

● いくつかの組合せの中からある特定のものを探すとき、考えるべき組合せが比較的少ない場合にはそれらを全て調べてしまうのが有効なことがある

● 「比較的少ない」: 106通りくらい

Page 9: 競技プログラミング練習会2015 Normal 第2回

全探索が有効な例

● 3×3魔法陣の解の個数を求める

● 3x3マスに1~9の数字を入れて魔法陣か確かめる

● 考えるべき組合せは9!通り

● 9! = 362880 < 106

● 間に合う

2 9 47 5 36 1 8

Page 10: 競技プログラミング練習会2015 Normal 第2回

全探索が有効な例

● 8クイーン問題の解のひとつを求める

● 8x8チェス盤に8つのクイーンを置く● どのクイーンも他の駒の利きにないよう置く

Page 11: 競技プログラミング練習会2015 Normal 第2回

8クイーン問題の解の一例

http://ja.wikipedia.org/wiki/エイト・クイーン

Page 12: 競技プログラミング練習会2015 Normal 第2回

全探索が有効な例

● 普通に並べると64C8 通り

– ちょっと大きい● 工夫が必要

Page 13: 競技プログラミング練習会2015 Normal 第2回

全探索が有効な例

● 普通に並べると64C8 = 4426165368 通り

– ちょっと大きい● 工夫が必要

Page 14: 競技プログラミング練習会2015 Normal 第2回

全探索が有効な例

● よく考えると、2つのクイーンは同じ列・同じ行には並ばない

● {1, 2, ... 8} の順列をクイーンの位置に対応させる

● 8! = 40320● 間に合う

(7, 5, 3, 1, 6, 8, 2, 4)

Page 15: 競技プログラミング練習会2015 Normal 第2回

全探索

● 考えるべき状態を木として扱うことが多い

1 2 3 4 5

1 31 2 1 4 2 1

Page 16: 競技プログラミング練習会2015 Normal 第2回

18 1914

深さ優先探索

● 葉に到達するまで子要素を探索してから次の子要素を探索する

1

2 11 15

3 5 8 12 13 16 17

4 6 7 9 10

Page 17: 競技プログラミング練習会2015 Normal 第2回

深さ優先探索

● スタックを使って深さ優先探索が実現できる● まだ探索していない頂点をスタックに積んでから、

スタックから頂点を取り出して深さ優先探索する

Page 18: 競技プログラミング練習会2015 Normal 第2回

18 1917

幅優先探索

● 子要素を全て探索してから子要素の子要素を探索する

● 根に近い順に探索していく

1

2 3 4

5 6 7 8 9 10 11

12 13 14 15 16

Page 19: 競技プログラミング練習会2015 Normal 第2回

幅優先探索

● キューを使って幅優先探索が実現できる● 今見ている頂点の子要素を全てキューに突っ込ん

でから、キューから頂点を1つ取りだして幅優先探索する

Page 20: 競技プログラミング練習会2015 Normal 第2回

深さ優先探索のココがすごい!

● メモリ消費量が幅優先に比べて少ない!● 再帰を使うことで楽に書ける!

– 再帰を使って書くときは、暗黙のうちにコールスタックを使っている

● 葉に至るまでが長い分枝が存在するとつらい!– 木の深さが有限でない場合終わらないことがある

Page 21: 競技プログラミング練習会2015 Normal 第2回

幅優先探索のココがすごい!

● ある種の最小解が求まる!– 例えば、頂点として迷路内の座標を設定しゴールに向

かって幅優先探索すると迷路を出るための最短経路が求まる

● (解があれば)(木の深さが有限でなくても)

必ず見つける!● メモリを大量に使う!

Page 22: 競技プログラミング練習会2015 Normal 第2回

累積和

Page 23: 競技プログラミング練習会2015 Normal 第2回

要求

● 長さNの配列が与えられて、その中にはN個の数が格納されている

● q個のクエリ(問題)が与えられるので、それに答える

– 問題: 「i番目からj番目までの要素の和を答えよ」

● N 10≦ 6 , q 10≦ 6

Page 24: 競技プログラミング練習会2015 Normal 第2回

愚直な解法

● 各クエリに対して、一つひとつ和を求めていく

Page 25: 競技プログラミング練習会2015 Normal 第2回

愚直な解法

● 各クエリに対して、一つひとつ和を求めていく● O(qN)

– 各クエリの処理に最大O(N)回の計算が必要

– クエリはq個

● qN 10≦ 12

– ちょっと大変

Page 26: 競技プログラミング練習会2015 Normal 第2回

累積和

● クエリを処理する前に予めSi = (0番目からi番目までの要素の和)を求めておく

● 各クエリに対してSj - Si-1を返せば良い

3 1 4 1 5 9 2 6 53 1 4 1 5 9 2 6 5

Page 27: 競技プログラミング練習会2015 Normal 第2回

累積和

● クエリを処理する前に予めSi = (0番目からi番目までの要素の和)を求めておく

● 各クエリに対してSj - Si-1を返せば良い

3 1 4 1 5 9 2 6 5

3 4 8 9 14 23 25 31 36

Page 28: 競技プログラミング練習会2015 Normal 第2回

計算量

● Siを求めるのにO(N)

– Si = Si-1 + ai を利用する

● 各クエリに対してO(1)● 従って、計算量はO(N + q)● 間に合う