AtCoder_Beginner_Contest_240_C Jumping Takahashi
いよいよDP.最初は優しめの問題から.
問題の説明
高橋君が回または段のジャンプを行います.回のジャンプの後ににいる可能性があるか答えなさい.
条件
- 入力は全て整数.
考えた事
まず,DPとは何をする手法なのか.端的に言えば値を記憶する配列を作り,それを使い回すものであろう.この問題においてそれは出来るのだろうか.
例えば1回目に段上がり,2回目に段上がったものと,1回目に段上がり,2回目に段上がったものでは当然現在位置が異なる.Greedyと違って今回は段まで上る最適な方法は存在しない(入力によって方法を変えなければならない)ため,段に辿り着けるかどうかは全てを試さなければ分からない.
ところがこれを全探索で実装しようとするとTLEエラーとなってしまう.全てのについてとを選んだ場合を考えると計算量は乗となり,が上限では計算が間に合わない.
なぜ全探索ではダメなのか.無駄が多いのである.最後だけとで異なるが,それ以外は全く同じものを選んだ場合を考えてもらいたい.全探索ではこれを一々計算し直すため,結果として非常に遅い手法となってしまう.
これを解消するのがDPである.配列に回目までに行ける部分を記憶させる事で,回目に行ける部分は回目までに行ける部分にまたはを足せば求められる.DPがメモ化再帰と呼ばれる所以である.*1裏を返せば回の状態を記憶させる事に意味のない問題の場合,DPは不適当な解法だといえる.
状態の遷移を上の図*2に表した.回目に座標にいる場合はを,そうでない場合はを記している.回目(登り始める前)はにいるため,にのみがつき,そうでない部分はが記される.
回目のジャンプを行うとまたはのジャンプを行う.ここで重要なのは回目のジャンプで行ける先を求める場合,回までの結果にそのまままたはを足せば求められるという点だ.これが可能であるからこそ,これを帰納的に回繰り返して回目に行ける場所が求められる.
少し前の繰り返しになるが,「繰り返し」が出来るかどうかがDPの使用可否を決める.どれを変数とすれば状態を遷移させられるか,繰り返しを作る事が出来るか,もし出来るならばどのような遷移となるか,これを考える事が非常に重要である.
競技プログラミングにおいては条件のところで変数のヒントが与えられている事も多い.今回においてパラメータ(変数)はとであり,は定数である.問題文にパラメータとしては既に記されており,後は自分でを置けばDPの遷移を考えられる.この知見は大事にしたい.
さあ実装だ.DPの実装は上の配列を埋める事に終始する.配列を埋めるにはどうすればいいのか.ここで出てくるのが漸化式である.
回目までの遷移と回目の遷移を結び付ける式があれば配列を埋めて行ける.DPの最終工程はこれである.
今回であればならばとなる.それ以外はとなる.つまり,
となる.まとめてコードにすると,
となる.プログラム上はからスタートしているため,の添え字がつずれているのには注意したい.を配列のデフォルトにし,とを動かして,上の条件を満たすものだけにをつけていく.
計算量に注目するとこれはであり,全探索のと比べ,大幅に短縮されている事が分かるだろう.
今回は制限に余裕があるため行っていないが,例えばをif文に追加し,以上となった場合は考えない*3ように実装すれば計算量はとなり更に短縮できる.
DPの流れをまとめると
- 全探索出来るかどうかを考える.(計算量が大きな基準となる)
- 状態が遷移するか,前の状態から今の状態を帰納的に導き出せるかを考える.(前の状態に足せるかどうかを見ると良さげ.)
- 変数を見つけ,小さな値の範囲でいいから配列を書いてみる.
- それを基に漸化式を作る.
- 漸化式を元に変数を動かし配列を全て埋めていく.
- 求めたい値に対応する配列を見て,答えを導き出す.
となるだろう.最後はもちろん問題によって異なるが今回であればである.これがかかがジャンプで辿り着けるかどうかを決める為,それを参照して出力すればいい.