微生物実験 (Bug Party)

Download Report

Transcript 微生物実験 (Bug Party)

2011/02/12 国立オリンピック記念青少年総合センター
第 10 回 日本情報オリンピック本戦 問題5
微生物実験 (Bug Party)
東京大学 理学部 情報科学科 4 年
メキシコ大会(2006) 日本代表
秋葉 拓哉 / [[iwi]]
問題概要
微生物
foo の放出と吸収
番号
1
2
3
4
5
6
放出量 ai
12
5
2
10
6
13
許容量 bi
8
9
4
12
7
9
5
10
6
放出
foo (有害物質)
5 + 10 + 6 = 21
できるだけ多く
選びたい
7
7
7
どれも死なない
シャーレ
(許容量:9, 12, 7)
摂取
O(N2) の解法
アイディア
foo 許容量 は,選ぶ
微生物の中で最低の物のみに注目
• foo 許容量が最低の奴が必ず最初に死ぬ
• 残りの奴らの許容量はどうでも良い
許容量の最低ラインを固定
許
最低
ライン
容
量
放出量
許容量の最低ラインを固定してみる?
選べる微生物が決まる
許
最低
ライン
容
量
放出量
赤色の部分の奴らだけ選べる
どれを選べばよいか?
許
最低
ライン
容
量
放出量
放出量が小さいやつから貪欲に,選べるだけ選ぶ
何故,貪欲的に選んで OK?
• 許される合計の放出量は選び方によらない
– foo 許容量の最低ラインを x とする
– k 匹選ぶなら,foo 放出量は合計 kx まで OK
• なら放出量の合計をできるだけ小さくしたい
→ 放出量が小さい方から選べば良い
愚直に実装 → 計算量は?
• foo 許容量の最低ラインの候補は?
– N 通り (b0, b1, …, bN)
• 最低ラインの各候補に対して
– 最高で N 個まで選ぶ
• よって O(N2)
O(N LOG2 N) の解法
アイディア
二分探索ができそう
• 許容量の最低ラインを固定したとき
– 放出量の小さい方から,どこまで選べるか?
– 「ここまで選べるか」が効率的に判定できれば OK
しかし,効率的に判定できるの…?
アイディア
最低ラインが少し変わった時,
状況も少ししか変わらない
1 つ選べる放出量が増えた
アイディア
二分探索ができそう
データ構造
を活用して実現
最低ラインが少し変わった時,
状況も少ししか変わらない
処理の順番
許
容
量
放出量
許容量の最低ラインをだんだん減らそう
実現したいこと
• 二分探索
– foo 放出量最低ラインを bi にしたとき
– 「何人入れられるか?」を効率的に求める
• 更新
– 新たに選べる放出量 ai を追加する
Binary Indexed Tree
• 数の列を管理するデータ構造
• O(log n) で,以下ができます
– ある場所に値を足す
– ある場所までの和 (累積和) を求める
Binary Indexed Tree
ビット演算を活用し,非常に簡潔に実装できる
Binary Indexed Tree
• 値の加算
void add(int i, int v) {
for (; i <= n; i += i & -i) bit[i] += v;
}
• 累積和の計算
int sum(int i, int *bit) {
int s = 0;
for (; i > 0; i -= i & -i) s += bit[i];
return s;
}
Binary Indexed Tree
• 値の加算
void add(int i, int v) {
for (; i <= n; i += i & -i) bit[i] += v;
}
• 累積和の計算
int sum(int i, int *bit) {
int s = 0;
for (; i > 0; i -= i & -i) s += bit[i];
return s;
}
Binary Indexed Tree
• 値の加算
君たちは
void add(int i, int v) {
for (; i <= n; i += i & -i) bit[i] += v;
}
• 累積和の計算
int sum(int i, int *bit) {
int s = 0;
for (; i > 0; i -= i & -i) s += bit[i];
return s;
}
Binary Indexed Tree
• 値の加算
君たちは
良い時代に
void add(int i, int v) {
for (; i <= n; i += i & -i) bit[i] += v;
}
• 累積和の計算
int sum(int i, int *bit) {
int s = 0;
for (; i > 0; i -= i & -i) s += bit[i];
return s;
}
Binary Indexed Tree
• 値の加算
君たちは
良い時代に
void add(int i, int v) {
for (; i <= n; i += i & -i) bit[i] += v;
}
• 累積和の計算
int sum(int i, int *bit) {
int s = 0;
for (; i > 0; i -= i & -i) s += bit[i];
return s;
}
生まれました!
Binary Indexed Tree の使い方
• 放出量をキーとする 2 本の BIT を管理
– Bit0: 微生物の個数
– Bit1: 微生物の放出量
放出量
Bit0
Bit1
1
0
0
2
1
2
3
0
0
4
1
4
5
1
5
6
0
0
7
1
7
8
0
0
...
...
...
Binary Indexed Tree の使い方
• 放出量 y のやつまで入れられるかな?
1. 放出量の和
– Bit1 の y までの累積和
1. 放出量の和
≦
2. 許せる放出量
ならOK
2. 許せる放出量
– 匹数は Bit0 の y までの累積和なので
– x × (Bit0 の y までの累積和)
O(n log2 n) の別解
• 何匹入れるかで二分探索
• k 匹入れると決めたら,
– やはり許容量の最低ラインを減らしながら
– 放出量が最低の k 匹を管理する
O(N LOG N) の解法
Binary Indexed Tree の
構造を活用する
二分探索を Binary Indexed Tree の構造に
沿って行うようにすればよい
おわり
お疲れ様でした