コンテストページ:https://atcoder.jp/contests/abc270
コンテスト中AC:A〜D
Eはコンテスト中は実装ミスで間に合いませんでしたが、コンテスト後に解けたので書いておきます。
D - Stones
石が個の状態からは、石が
個の状態に遷移します。
そのため、石が少ない時の状態から順に高橋くんが取れる石の個数を決定していくことができれば、くらいで解けそうだということがわかります。
普通のループを使ったDPでも解けそうですが、コンテストではメモ化再帰を使いました。
メモ化再帰にあたり、
石が残り
個の状態で、高橋くんが取れる石の最大個数
とおいて計算していきたいところですが、高橋くんのターンか青木くんのターンかで、戦略が異なります。
すなわち、
- 高橋くんは、高橋くんが取れる石の個数を最大化したい
- 青木くんは、高橋くんが取れる石の個数を最小化したい
そのため、どちらのターンなのかという情報を持たせます。
石が残り
個、ターンの状態が
で、高橋くんが取れる石の最大個数。但し、
で、
の時は高橋くんのターンで、
の時は青木くんのターン。
それでは、遷移を考えます。
各ターンにおいて、を
かつ、
を満たす最大値とします。
高橋くんのターンの場合
取れる石の個数を最大化します。また、石を取った時に、高橋くんがとれる石の個数に加算できるので、以下のようになります。
青木くんのターンの場合
取れる石の個数を最小化するので、以下のようになります。
遷移は以上です。
また、再帰の終了条件として、石が個の時の値、
を設定しておきます。
計算量は最初にも書きましたが、各個数において回の計算となるので、
と思います。
Submission #35156369 - TOYOTA MOTOR CORPORATION Programming Contest 2022(AtCoder Beginner Contest 270)
↑はコンテスト後に微修正した実装です。
E - Apple Baskets on Circle
最終的な移動回数をとし、
を
で割った商を
、余りを
とします。これは、
周してから残り
回移動するという風に考えられます。
であることから、
を求められれば、残り
回はシミュレーションできます。
を求める方法を考えます。
例としてを考えます。
1週目
となり、食べたりんごの数に
個加算される。
2週目
となり、食べたりんごの数に
個加算される。
3週目
となり、食べたりんごの数に
個加算される。
4週目
となり、食べたりんごの数に
個加算される。
5週目
となり、食べたりんごの数に
個加算される。
加算するりんごの数の変化点に注目すると、
- 最初の
の最小値である
が
となるまで、空でないかごの個数分
個を加算する
が空になった時点の
の最小値である
が空となるまで、空でないかごの個数分
個加算する
が空になった時点の
の最小値である
が空となるまで、空でないかごの個数分
個加算する
このように、りんごの数が最小となるかごが空になるまでは同じ分だけりんごの数が加算されるので、まとめて考えることができます。
そこで、以下のように操作していくと、食べたりんごの数と現在何周したかを計算していくことができます。
から最小値
を取得する。ここから
周して、合計
個のりんごを食べる。その後、
の
番目の要素を削除し、全ての要素から
を引く。
最小値の取得や値の削除は優先度付きキューにより実現できます。
あとは、を引く操作に時間がかかるので、今までに何周したのかというのを
に記録します。すると、以下のようにできます。
から最小値
を取得する。
周して、
個のりんごを食べたことにする。その後、
と更新し、
の
番目の要素を削除する。
さて、この操作を繰り返すと、どこかの時点で「次の操作をすると食べたりんごの数が以上となる」状態になります。
この状態からあと何周必要かを計算します。残りの周回数は、この時点でのの最小値以下であることが保証されます。
この時点からの残りの周回数をとおくと、以下の不等式が成り立てばよいです。
よって、
です。
よって、先ほどの操作と今回の操作で得られた値を合計すれば、が求まります。
あとは、周した状態(元の
のすべての要素から
を引いた状態)からりんごの数が
となるまでシミュレーションすればよいです。
長くなりましたが、要点をまとめるとこのような流れになります。
の最小値が0になるまで周回するという操作を繰り返す
- 上記の操作でりんごの食べた数が
以上となる直前で操作を終了し、残り何周必要かを計算する
- 全体で何周すれば良いかがわかったので、その分だけ周回した後から、残りをシミュレーションする