C++の練習を兼ねて, AtCoder Beginner Contest 023 の 問題D (D – 射撃王) を解いてみた.
■感想.
1. 解答方針がよく分からなかったので, 解説から類推する形で解き直しした.
2. 二分探索を実装する部分で, 終了条件をどうすればよいか分からず, 手こずった.
=> これを解決するために, ゴリゴリ書く羽目になったものの, 以下の二点を対応したところ, AC版とすることが出来た.
① 風船の情報を, 構造体に保管して, 再利用できるようにした
② 確認対象としている高度で, 風船を割る方法が存在するかをチェックする関数の追加を行った
本家のサイトAtCoder Beginner Contest 023 解説をご覧下さい.
■C++版プログラム(問題D/AC版).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
// 解き直し. // ABC023 解説. // https://www.slideshare.net/chokudai/abc023 #include <bits/stdc++.h> using namespace std; using LL = long long; const LL MAX = 1e18 + 1; struct balloon { LL h; // 風船の高度. LL s; // 風船の速度. double t; // 制限時間. bool operator < (const balloon &B) const { return t < B.t; } }; // 確認対象としている高度で, 風船を割る方法が存在するかをチェック. // @param v: 風船に関する情報. // @param opt: 確認対象の高度. // @return : 風船を割る方法の存在確認. // true : 風船を割る方法が存在する, false : 風船を割る方法が存在しない. bool isExist(vector<balloon> v, LL opt){ bool ret = true; for(int i = 0; i < v.size(); i++){ double bHeight = v[i].h + v[i].s * i + 0.0; // i秒後 の 風船の高さが, 確認対象の高度 を 超えていたら, 風船を割る方法が存在しないと判定. if(opt < bHeight){ // cout << "i=" << i << " v[i].h=" << v[i].h << " v[i].s=" << v[i].h << " bHeight=" << bHeight << endl; ret = false; break; } } return ret; } int main() { // 1. 入力情報取得. int N; cin >> N; LL H[N], S[N]; vector<balloon> v; for(int i = 0; i < N; i++){ balloon b; cin >> b.h >> b.s; v.push_back(b); } // 2. 解説通り. // 高さを固定して, 制限時間で, sort. // -> 二分探索で, opt を 探す LL hi = MAX; LL lo = 1; LL opt = MAX; int counter = 0; // 無限ループ防止のため, カウンター入れる. while(counter < 100){ // 2-1. 制限時間を保存. for(int i = 0; i < N; i++){ double t = (opt - v[i].h) / v[i].s; v[i].t = t; } // 2-2. sort. sort(v.begin(), v.end()); // 2-3. hi, lo, opt 更新. // if(counter > 98) cout << " hi=" << hi << " lo=" << lo << " opt=" << opt << endl; bool b = isExist(v, opt); if(b) hi = (lo + hi) / 2LL; else lo = (lo + hi) / 2LL + 1; opt = (hi + lo) / 2; // 2-4. カウンター を インクリメント. counter++; // 2-5. 終了. if(hi - lo <= 0) break; } // 3. 出力. cout << opt << endl; return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
[入力例] 4 5 6 12 4 14 7 21 2 [出力例] 23 ※AtCoderテストケースより [入力例] 6 100 1 100 1 100 1 100 1 100 1 1 30 [出力例] 105 ※AtCoderテストケースより [入力例] 10 10 5 12 8 8 15 4 12 30 7 45 6 18 12 22 13 17 8 25 5 [出力例] 68 具体的には, 競技開始から 0秒後 に 風船6 を割る, 風船6 の ペナルティは 45 + 6 × 0 = 45 競技開始から 1秒後 に 風船8 を割る, 風船8 の ペナルティは 22 + 13 × 1 = 35 競技開始から 2秒後 に 風船5 を割る, 風船5 の ペナルティは 30 + 7 × 2 = 44 競技開始から 3秒後 に 風船7 を割る, 風船7 の ペナルティは 18 + 12 × 3 = 54 競技開始から 4秒後 に 風船3 を割る, 風船3 の ペナルティは 8 + 15 × 4 = 68 競技開始から 5秒後 に 風船4 を割る, 風船4 の ペナルティは 4 + 12 × 5 = 64 競技開始から 6秒後 に 風船9 を割る, 風船9 の ペナルティは 17 + 8 × 6 = 65 競技開始から 7秒後 に 風船2 を割る, 風船2 の ペナルティは 12 + 8 × 7 = 68 競技開始から 8秒後 に 風船10を割る, 風船10の ペナルティは 25 + 5 × 8 = 65 競技開始から 9秒後 に 風船1 を割る, 風船1 の ペナルティは 10 + 5 × 9 = 55 |
■参照サイト
AtCoder Beginner Contest 023