第 10 回 日本情報オリンピック本戦 問題 5 微生物実験 (bug party)
DESCRIPTION
2011/02/12 国立オリンピック記念青少年総合センター. 第 10 回 日本情報オリンピック本戦 問題 5 微生物実験 (Bug Party). 東京大学 理学部 情報科学科 4 年 メキシコ大会 (2006) 日本代表 秋葉 拓哉 / [[ iwi ]]. 問題概要. 微生物. foo の放出と吸収. 放出. 5. 10. 6. foo ( 有害物質 ) 5 + 10 + 6 = 21. できるだけ多く 選びたい. 7. 7. 7. 摂取. どれ も死なない ( 許容量: 9, 12, 7). シャーレ. - PowerPoint PPT PresentationTRANSCRIPT
第 10 回 日本情報オリンピック本戦 問題 5
微生物実験 (Bug Party)
東京大学 理学部 情報科学科 4 年メキシコ大会 (2006) 日本代表
秋葉 拓哉 / [[iwi]]
2011/02/12 国立オリンピック記念青少年総合センター
問題概要
番号 1 2 3 4 5 6
放出量 ai12 5 2 10 6 13
許容量 bi8 9 4 12 7 9
シャーレ
微生物
できるだけ多く選びたい
foo ( 有害物質 )
5 + 10 + 6 = 21
放出5 10 6
7 7 7摂取
どれも死なない ( 許容量: 9, 12, 7)
foo の放出と吸収
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: 微生物の放出量
放出量 1 2 3 4 5 6 7 8 ...
Bit0 0 1 0 1 1 0 1 0 ...Bit1 0 2 0 4 5 0 7 0 ...
Binary Indexed Tree の使い方
• 放出量 y のやつまで入れられるかな?
1. 放出量の和– Bit1 の y までの累積和
2. 許せる放出量–匹数は Bit0 の y までの累積和なので– x × (Bit0 の y までの累積和 )
1. 放出量の和≦
2. 許せる放出量なら OK
O(n log2 n) の別解
• 何匹入れるかで二分探索
• k 匹入れると決めたら,–やはり許容量の最低ラインを減らしながら–放出量が最低の k 匹を管理する
O(N LOG N) の解法
Binary Indexed Tree の構造を活用する
二分探索を Binary Indexed Tree の構造に沿って行うようにすればよい
おわりお疲れ様でした