AGC005 A - STring
解法
この手の問題は、スタックをすぐに思い浮かべることができれば勝ちです。
次のようにシミュレーションしていけばいいです。
次に見る文字がだった場合
問答無用でスタックにプッシュします。次に見る文字がだった場合
スタックのトップをまず確認します。トップがであれば、スタックからその文字を取り出して、操作を終了します。
スタックが空、もしくはトップがであれば、次のをスタックにプッシュして操作終了です。
これを最初から最後まで行い、最終的にスタックに残っている文字の個数が答えとなります。
感想
つい最近、括弧列の問題でスタックを利用するものを見たので、さらっと解くことができました。
2種類の文字列で、ある文字に別の文字を対応させる系は、スタックを活用すると楽ですね。
ABC046(ARC062) D - AtCoDeerくんと変なじゃんけん / AtCoDeer and Rock-Paper
解法
自分はシミュレーションをしました。
相手がを出してきたときは、必ずこちらもをだして相手にポイントが渡らないようにし、そして余ったを出せる回数で得点を稼ぐ、といった方針です。
ということで、まずは相手のの回数をカウントします。これをとします。
そして、前からシミュレーションします。今自分がを連続で出すことができる回数をとします。今見ているターンで、相手が
を出した場合
あいこにするために、自分もを出します。とのカウントを1つ減らします。を出した場合
だった場合、得点に1追加し、を1減らします。
それ以外のパターンでは、のカウントを1増やします。
このシミュレーションを最後まで行えば、答えが求まります。
ですが、実際はこのあたりのシミュレーションはどうでもよくて、が答えになります。
最悪のパターンである、自分がすべてグーを出した場合の得点はであり、ここから、自分がパーを出す回数を1増やすと、常に得点は1増えるため、パーを出せるだけ出すのが最善であり、最終的に上の数式が答えになります。
と解説に書いてありました。
感想
C問題同様、最終的な答えは綺麗な式になりますが、結局自分はシミュレーションをしていました。D問題は、式を出すよりもシミュレーションをした方がおそらくはやく解けるので、まぁどちらでもよかったかなと思っています。
思考の過程としては、まず相手に得点を取られないようにするにはどうすればいいか、を考え、そのためには相手がパーを出したタイミングで常に自分がパーを出せばいいことがわかり、そのうえで自分が得点を重ねていくためにどのような動きをすればいいか?を考えると、上のようなシミュレーションにたどり着きました。
ABC046 C - AtCoDeerくんと選挙速報 / AtCoDeer and Election Report
問題
提出コード1
提出コード2
GCD求める関数書いてますが関係ないです。
解法
提出コード1は、単純に場合分けしていきます。
現在の高橋君の票数を、青木君の票数をとします。
まず、1手前と比率が同じであった場合
その回をスルーします。高橋君、もしくは青木君の票数を変えずに条件を満たすことができる場合
つまり、かつとなる場合、もしくはとを入れ替えたパターンが成り立つ場合です。
これは素直にとすればよいです。それ以外
とを計算します。
高橋君の票数が前者の値になるか、青木君の票数が後者の値になるか、の2択です。
まずは、票数が減るパターン、すなわち
、もしくはとなるパターンを排除します。これらのパターンになってしまった場合は、そうならない方を選択する必要があります。
これらのパターンにならない場合は、二人の票数を改めて計算し、票数が少なる方を選択すれば答えとなります。
想定解の提出コード2は、すごい綺麗な解法です。
かつが成り立つ最小のを求めればよいので、
を求めていけばよいです。
これを回繰り返せば、あとはを計算して答えとなります。
感想
過去に解いたときも、今回も、提出コード1のような解法で出しました。
2回とも、かなりバグらせているので、とても反省しています。
この手の問題で綺麗な立式ができるようになりたいですね…
AOJ 2933 - 矢 / Arrow (RUPC2019 Day3 D)
解法
簡単のため、にも送風機があるとしてよいです。
矢の長さがのときの損失回数がわかっていれば、各クエリに対して二分探索をして答えを求めることができます。
ということで、長さがのときの損失回数を頑張って求めます。
送風機と送風機の間にすっぽり矢が収まると、損失を受けます。
例えば、送風機と送風機の間の長さが
なので、その区間で長さの矢に対しておこる損失の回数は、
となります。
矢の長さが増えると、その区間でおこる損失の回数は1減ります。損失回数が0になるまでこの法則は成り立ちます。
矢の長さがのとき、損失回数は送風機間の長さのみに依存するため、送風機間の長さを昇順でソートしても問題ありません。ということでソートをします。
さて、から順番に損失回数を計算していきます。なぜかというと、の場合は明らかになので、すぐに求めることができるからです。
の長さの矢に対する損失回数を、の際の情報を元に計算します。
長さの矢に対する損失回数をとすると、
となります。
あとはこれを求めていけばよいです。が、コーナーケースがただ一つ存在し、スタートから一番最初の送風機までの区間です。
この区間では、矢の長さがどのような場合でも、最初の送風機までの区間分の損失が生じます。
この損失を追加し忘れないようにしましょう。
あとは、前計算したものをもとに、二分探索をすることで答えを求めることができます。
感想
なんとなーくやりたいことは浮かんでいましたが、バグの祭りでした。
なんとなく、はじめからこうなるであろうことは予測していましたが…
途中でチームメイトのすずくんがコーナーケースに気づいてくれたので感謝です。
AOJ 2931 - 括弧を語る数 / Parentheses Number (RUPC2019 Day3 B)
解法
まず、順列に逆の情報を持たせます。すなわち、
番目の開き括弧が 番目の閉じ括弧に対応しているとき、 数列の番目の値は である。
というようにします。
すると、スタックを利用して、次のような操作を行うことで、問題の条件を満たす括弧列を作成することができます。
まず、今見ている配列の添え字を、次に出現する閉じ括弧が番目であるとします。
スタックにをプッシュします。と同時に、答えとなる文字列に"("を追加します。
スタックの一番上と、を比較します。数字が同じであれば、スタックからその数字を取り出して捨てます。そして、を1増やして、に")"を追加します。
2の操作が行える限り行います。スタックが空になるか、数字が一致しなくなったら1の操作に戻ります。をすべてプッシュし終えていたら、全ての操作を終了します。
こうしてできたが答えとなります。ただし、スタックの中身が空になっていなければ、問題の条件を満たす文字列が無かったことになり、":("を出力することになります。
感想
括弧列といえばスタックを頭の片隅に入れている気がします。
今回も片隅においてはいたのですが、うまい処理を思いつけずにいたので、先輩にヒントをいただきました。
再帰でやろうとしていたので、こっちの方がおそらくシンプルに実装できたと思います。
AOJ 3057 - First Kiss (RUPC2019 Day2 G)
解法
まずは、キッポーが1本のときについて考えます。
この問題についてのグランディ数を求めることができれば、あとは本のキッポーについてのグランディ数のxorを取った結果を見ればよいです。
キッポーの長さを0にした方が負けなので、1だけ残して相手のターンにまわすことができれば勝つことができます。
ということで、長さが1の際を最終地点として考えていきます。
基本的に、の長さだけ1ターンで食べることができますので、
の長さで自分のターンになれば、長さを1にして相手のターンに回すことができます。
逆に、現在の長さがのときは、の任意の長さにすることができます。
これを踏まえると、長さがのときのグランディ数は、
となります。
これが0なら後手の勝ち、そうでなければ先手の勝ちです。
あとは、本のキッポーについて、先ほどの式でグランディ数を計算し、xorをとっていけば、1本の際と同様の判定で勝者を調べることができます。
感想
小学生かそこらのころ、0からスタートして、交互に1~3加えた数字を言っていき、21だか23になったら負け、というゲームをしたことがあります。
確か何かの新聞の広告で、このゲームについての必勝法を考えよう!みたいな問題が載っており、似たようなことを考えました(グランディ数とかそういうのは知らないので、どの数字を言ったら確実に負けてしまうのか、とかそういったあいまいな話ですが)。
グランディ数の問題にしては割とシンプルなため、珍しく自力で解くことができました。うれしいです。
ちなみに、この記事を全て書き終えてから、キッポーがポッキーになっていたことに気づいて慌てて直したのは秘密です。
AOJ 3058 - Ghost (RUPC2019 Day2 H)
解法
燃やす埋める問題です。
詳しくはこちらを。
始点と終点および各幽霊に対応する作り、から各幽霊、各幽霊からに辺をはります。
この際、から出てくる辺を左に向くパターン、へ向かう辺を右に向くパターンとすると、
今番目の幽霊が向いている向きが
右のとき
から出てくる辺は容量、へ向かう辺は容量左のとき
から出てくる辺は容量、へ向かう辺は容量
となるように辺をはればよいです。
そして、番目と番目の幽霊が向かい合っているときにの恐怖度が発生するとしたとき(とします)、
からに容量の辺をはると、うまく条件を表現することができます。
あとは、フローを流していき、最小カットを求めれば答えとなります。
感想
問題を見た時点で燃やす埋める問題だ!となったので、成長を感じます。
こういう気づけばあとはコピペで終わるような問題をさらっと解けるようになると気持ちいいですね。