競技プログラミング練習会2015 Normal 第1回
-
Upload
hideaki-nagamine -
Category
Engineering
-
view
98 -
download
0
Transcript of 競技プログラミング練習会2015 Normal 第1回
アルゴリズムと計算資源
● アルゴリズムを実行するのに必要なリソース(メモリとか時間とか)は入力によって変わってくる
– 例えば「nを素因数分解せよ」という問題を解くアルゴリズムを考えた場合、nが大きくなるほど計算に時間がかかりそう
● 入力に応じて必要な計算資源の量がどう変化するかを考察したい– プログラムへの入力を引数にとって実行に必要な計算資源
の量を返す関数を考えよう
計算量
● アルゴリズムを実行する時に必要になる計算資源の量– 時間計算量
● 実行に必要な時間の長さ– 空間計算量
● 実行に必要な記憶領域の大きさ– もっと詳しくアルゴリズムを分析する時には最悪時間計算量
とか平均時間計算量とか色々な計算量を考えます● 「時間計算量が小さいアルゴリズム」は「時間効率の良
いアルゴリズム」、みたいなイメージ● 普通計算量と言えば時間計算量を指す
ランダウの記号
ある計算量がO(n2)であるとは、
その計算量を表す関数f(n)に対して
であること。
→「nが大きいときは、f(n)はn2より速いペースで大きくなっていったりしない」的な意味
∃N ,∃ k ;n>N ⇒ f (n)<k n2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 210
500
1000
1500
2000
2500
3000
入力
計算
量
計算量のイメージ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 210
500
1000
1500
2000
2500
3000
入力
計算
量
計算量のイメージn2
kn2
f(n)
ランダウの記号
● 定数倍は無視する– O(2n) = O(n)
– O(log2 n) = O(log10 n)
● 小さい項は無視する– O(1000 + 50n + n²) = O(n²)
– O(n + log n) = O(n)
– O(en + n100) = O(en)
競技プログラミングとランダウの記号
● nに具体的な値を代入して制限時間に間に合うか確かめることが多い– 1秒間にできる処理は多くて1億回くらい
– 例: 「計算量がO(MN)でM 5000, N 100≦ ≦ だから十分間に合う」
ソートと計算量
● ソートを行うアルゴリズムを考えます– ソートは、数のリストを入力にとって小さい順に並べ替え
たリストを返す問題のこと● 比較回数と交換回数を計算量とすることが多い● 計算量のパラメータはリストの長さn
– 長いリストほど並べ替えに時間がかかる● 効率がいいやつと悪いやつがある
ボゴソート
● リストを適当にシャッフルして小さい順に並んでいるか確かめる
● 上の操作を小さい順に並ぶまで繰り返す
● 並べ方は高々n!通りしかないので、毎回異なる並べ方をするようにしておけばO(n×n!)でソートできる
マージソート
● 2つのソートされたリストを併合して1つのソートされたリストにすることをマージという
● マージを使ってソートするのがマージソート
2 4 7 8 1 5 6 9
1 2 4 5 6 7 8 9
マージソート
● 2つのソートされたリストを併合して1つのソートされたリストにすることをマージという
● マージを使ってソートするのがマージソート● リストの長さがm, nのとき計算量はO(m + n)
– 2つのリストの先頭を見比べて、小さい方を取っていく
マージソートの計算量
● マージソートの計算量を表す関数をf(n)とすると、
f(2N+1) = f(2N) + f(2N) + 2×2N
が成り立つ。– 部分リスト(長さ2N)のマージソートにf(2N)
– 2リストのマージ(合計要素数2×2N)に2×2N
● f(2N) = 2N log2(2N)が分かる
● 従って、fはO(n log n)
– O(n2)よりちょっと速くなった
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 210
50
100
150
200
250
300
350
400
450
n²
10nlogn
A Thief
● お宝と、お宝を詰める風呂敷がある● お宝はそれぞれ重さと価値が決まっている● 風呂敷には合計重量Wまでお宝を詰められる● 上記の制限のもとで、風呂敷に詰めたお宝の価値
を最大にする– 実際にはそのときの重さも出力する必要がありますが、
この説明では省略します
全探索
● 各お宝に対して「風呂敷に詰める」か「詰めない」かを決めて、合計重量を計算する
● 重量をオーバーしていたら無視● オーバーしていなければ合計価値を計算し、最大
値と比較● 再帰関数で実装するとコード量が少ないので良い
– solve(i, w)を「i番目以降のお宝を容量wの風呂敷にできるだけ詰めたときの最大の価値」とする
実装
int solve(int i, int w) {if (i == N 1) {
return (weight[i] > w ? 0 : value[i]);} else if (weight[i] > w) {
return solve(i + 1, w);} else {
return max(value[i] + solve(i + 1,w weight[i]),solve(i + 1, w));
}} i 今注目している品物の番号
w 風呂敷の残り容量value[i] i番目のお宝の価値weight[i] i番目のお宝の重量
実装
int solve(int i, int w) {if (i == N 1) {
return (weight[i] > w ? 0 : value[i]);} else if (weight[i] > w) {
return solve(i + 1, w);} else {
return max(value[i] + solve(i + 1,w weight[i]),solve(i + 1, w));
}}
全探索
● 考えるべき「詰める」「詰めない」の組合せは2N通り
● これらを全て探索しているので、計算量はO(2N)
– solveの再帰が1段深くなる毎に必要な計算の量がだいたい2倍になっている
● Nは最大1000なので全然間に合わない
– 21000 > 10300
考察
● 無駄な計算をしていないか?● solveは同じ引数に対して同じ値を返す● 重量が同じ品物の組合せがあると、同じ計算が何
回も行われることになる
1weight
value 5
6 2 3 4 1 ・・・
3 2 4 7 1
i 0 1 2 3 4 5
・・・
・・・
考察
● 無駄な計算をしていないか?● solveは同じ引数に対して同じ値を返す● 重量が同じ品物の組合せがあると、同じ計算が何
回も行われることになる
1weight
value 5
6 2 3 4 1 ・・・
3 2 4 7 1
i 0 1 2 3 4 5
・・・
・・・
この後、solve(6, W-6)が計算される
考察
● 無駄な計算をしていないか?● solveは同じ引数に対して同じ値を返す● 重量が同じ品物の組合せがあると、同じ計算が何
回も行われることになる
1weight
value 5
6 2 3 4 1 ・・・
3 2 4 7 1
i 0 1 2 3 4 5
・・・
・・・
この後、solve(6, W-6)が計算される
考察
● 無駄な計算をしていないか?● solveは同じ引数に対して同じ値を返す● 重量が同じ品物の組合せがあると、同じ計算が何
回も行われることになる
1weight
value 5
6 2 3 4 1 ・・・
3 2 4 7 1
i 0 1 2 3 4 5
・・・
・・・
この後、solve(6, W-6)が計算される
実装
map<pair<int,int>, int> memo;
int solve(int i, int w) { if (memo.find(make_pair(i, w)) != memo.end()) { return memo[make_pair(i, w)]; } else if (i == N 1) { return (weight[i] > w ? 0 : value[i]); } else if (weight[i] > w) { memo[make_pair(i, w)] = solve(i + 1, w); return memo[make_pair(i, w)]; } else { int use = value[i] + solve(i + 1, w weight[i]), no_use = solve(i + 1, w); memo[make_pair(i, w)] = max(use, no_use); return memo[make_pair(i, w)]; }}
実装
map<pair<int,int>, int> memo;
int solve(int i, int w) { if (memo.find(make_pair(i, w)) != memo.end()) { return memo[make_pair(i, w)]; } else if (i == N 1) { return (weight[i] > w ? 0 : value[i]); } else if (weight[i] > w) { memo[make_pair(i, w)] = solve(i + 1, w); return memo[make_pair(i, w)]; } else { int use = value[i] + solve(i + 1, w weight[i]), no_use = solve(i + 1, w); memo[make_pair(i, w)] = max(use, no_use); return memo[make_pair(i, w)]; }}
保存されてたら計算しない
実装
map<pair<int,int>, int> memo;
int solve(int i, int w) { if (memo.find(make_pair(i, w)) != memo.end()) { return memo[make_pair(i, w)]; } else if (i == N 1) { return (weight[i] > w ? 0 : value[i]); } else if (weight[i] > w) { memo[make_pair(i, w)] = solve(i + 1, w); return memo[make_pair(i, w)]; } else { int use = value[i] + solve(i + 1, w weight[i]), no_use = solve(i + 1, w); memo[make_pair(i, w)] = max(use, no_use); return memo[make_pair(i, w)]; }}
計算したら保存する
メモ化再帰
● 各(i, w)に対する計算は高々1回しか行われない
● 計算すべき(i, w)の組合せは最大NW通り
● mapへアクセスするためにかかる時間はmapの要素数xに対してO(log x)– x NW≦ なのでO(log NW)
● 以上より、この解法の計算量はO(NW log NW)– N 1000, W 1000≦ ≦
– 間に合う
動的計画法
● よく考えると、わざわざ再帰的に書く必要はない● mapの代わりに2次元配列tableを用意して、
table[i][w]を「i番目以降のお宝を容量wの風呂敷にできるだけ詰めたときの最大の価値」とする
● table上で2重ループを使いtable[i][w]を計算する● 再帰しなくてもループで同じことを実現できる!!
実装
int solve() { for (int i = N 1; i >= 0; i) { for (int w = 0; w <= W; w++) { if (w >= weight[i]) { table[i][w] = max(table[i+1][w], value[i] + table[i+1][wweight[i]]); } else { table[i][w] = table[i+1][w]; } } } return table[0][W];}
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3
4
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3
4 0
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3
4 0 0
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3
4 0 0 0
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3
4 0 0 0 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3
4 0 0 0 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3
4 0 0 0 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3 0
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3 0 0
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3 0 0 0
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3 0 0 0 10
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3 0 0 0 10 10
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3 0 0 0 10 10 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2
3 0 0 0 10 10 210 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2 0
3 0 0 0 10 10 210 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2 0 0
3 0 0 0 10 10 210 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2 0 0 0
3 0 0 0 10 10 210 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2 0 0 0 120
3 0 0 0 10 10 210 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2 0 0 0 120 120
3 0 0 0 10 10 210 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2 0 0 0 120 120 210
3 0 0 0 10 10 210 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1
2 0 0 0 120 120 210 210
3 0 0 0 10 10 210 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0
1 0 0 100 120 120 220 220
2 0 0 0 120 120 210 210
3 0 0 0 10 10 210 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0 0 60 100 160 180 220 280
1 0 0 100 120 120 220 220
2 0 0 0 120 120 210 210
3 0 0 0 10 10 210 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10
何がどうなっているんだ
6560,1100,2120,3210,410,4
i\w 0 1 2 3 4 5 6
0 0 60 100 160 180 220 280
1 0 0 100 120 120 220 220
2 0 0 0 120 120 210 210
3 0 0 0 10 10 210 210
4 0 0 0 10 10 10 10
i 0 1 2 3 4
weight 1 2 3 5 3
value 60 100 120 210 10