290 likes | 351 Views
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). シャーレ.
E N D
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) シャーレ
アイディア • foo許容量が最低の奴が必ず最初に死ぬ • 残りの奴らの許容量はどうでも良い foo許容量 は,選ぶ 微生物の中で最低の物のみに注目
許容量の最低ラインを固定 許容量の最低ラインを固定してみる? 最低 ライン
選べる微生物が決まる 赤色の部分の奴らだけ選べる 最低 ライン
どれを選べばよいか? 放出量が小さいやつから貪欲に,選べるだけ選ぶ 最低 ライン
何故,貪欲的に選んで OK? • 許される合計の放出量は選び方によらない • foo許容量の最低ラインを xとする • k匹選ぶなら,foo放出量は合計 kxまで OK • なら放出量の合計をできるだけ小さくしたい → 放出量が小さい方から選べば良い
愚直に実装 → 計算量は? • foo許容量の最低ラインの候補は? • N通り (b0, b1, …, bN) • 最低ラインの各候補に対して • 最高で N個まで選ぶ • よって O(N2)
アイディア 二分探索ができそう • 許容量の最低ラインを固定したとき • 放出量の小さい方から,どこまで選べるか? • 「ここまで選べるか」が効率的に判定できれば OK しかし,効率的に判定できるの…?
アイディア 最低ラインが少し変わった時, 状況も少ししか変わらない 1 つ選べる放出量が増えた
アイディア 二分探索ができそう データ構造 を活用して実現 最低ラインが少し変わった時, 状況も少ししか変わらない
処理の順番 許容量の最低ラインをだんだん減らそう
実現したいこと • 二分探索 • foo放出量最低ラインを biにしたとき • 「何人入れられるか?」を効率的に求める • 更新 • 新たに選べる放出量 aiを追加する
Binary Indexed Tree • 数の列を管理するデータ構造 • O(log n) で,以下ができます • ある場所に値を足す • ある場所までの和(累積和)を求める
Binary Indexed Tree ビット演算を活用し,非常に簡潔に実装できる
Binary Indexed Tree • 値の加算 • 累積和の計算 void add(inti, intv) {for (; i <= n; i += i & -i) bit[i] += v;} int sum(inti, int *bit) {ints = 0;for (; i > 0; i -= i & -i) s += bit[i];return s;}
Binary Indexed Tree • 値の加算 • 累積和の計算 !? void add(inti, intv) {for (; i <= n; i += i & -i) bit[i] += v;} int sum(inti, int *bit) {ints = 0;for (; i > 0; i -= i & -i) s += bit[i];return s;}
Binary Indexed Tree • 値の加算 • 累積和の計算 君たちは void add(inti, intv) {for (; i <= n; i += i & -i) bit[i] += v;} int sum(inti, int *bit) {ints = 0;for (; i > 0; i -= i & -i) s += bit[i];return s;}
Binary Indexed Tree • 値の加算 • 累積和の計算 君たちは void add(inti, intv) {for (; i <= n; i += i & -i) bit[i] += v;} 良い時代に int sum(inti, int *bit) {ints = 0;for (; i > 0; i -= i & -i) s += bit[i];return s;}
Binary Indexed Tree • 値の加算 • 累積和の計算 君たちは void add(inti, intv) {for (; i <= n; i += i & -i) bit[i] += v;} 良い時代に int sum(inti, int *bit) {ints = 0;for (; i > 0; i -= i & -i) s += bit[i];return s;} 生まれました!
Binary Indexed Tree の使い方 • 放出量をキーとする 2 本の BIT を管理 • Bit0: 微生物の個数 • Bit1: 微生物の放出量
Binary Indexed Tree の使い方 • 放出量 yのやつまで入れられるかな? • 放出量の和 • Bit1 の yまでの累積和 • 許せる放出量 • 匹数は Bit0 の yまでの累積和なので • x × (Bit0 の yまでの累積和) 1. 放出量の和 ≦ 2. 許せる放出量 ならOK
O(n log2n) の別解 • 何匹入れるかで二分探索 • k匹入れると決めたら, • やはり許容量の最低ラインを減らしながら • 放出量が最低の k匹を管理する
Binary Indexed Tree の構造を活用する 二分探索を Binary Indexed Tree の構造に 沿って行うようにすればよい
おわり お疲れ様でした