yukicoder No.533 Mysterious Stairs
前日にtesterしました.
問題概要
No.533 Mysterious Stairs - yukicoder
- N段の階段がある.
- 1回の飛躍で1step,2step,3stepずつ登れる.
- 同じstep数を連続して使えない
- N段目に到達するパターン数を出力せよ.
基本解法
4つの状態が考えられる.
- まだジャンプしていない.
- 1段ジャンプしたので,次は1段ジャンプ出来ない.
- 2段ジャンプしたので,次は2段ジャンプ出来ない.
- 3段ジャンプしたので,次は3段ジャンプ出来ない.
0の状態はスタート時以外では発生し得ないので,1回ジャンプした状態を初期状態とすることで,0の状態を排除出来る.
あとは残り3つの状態について,どう遷移するのかを考えればいい.
今,段目に居るとして,
- 段目にジャンプできるのは状態2,状態3.
- 段目にジャンプできるのは状態1,状態3.
- 段目にジャンプできるのは状態1,状態2.
これをコードとして書けばAccept.
ll solve_MLE(int n) { vector<array<ll, 4>> dp(n + 4); dp[1][1] = 1; // 1jump で 1step 登る.次は 1jump は使えない. dp[2][2] = 1; // 2jump で 2step 登る.次は 2jump は使えない. dp[3][3] = 1; // 3jump で 3step 登る.次は 3jump は使えない. for (int i = 1; i < n; ++i) { // +1jumpする. dp[i + 1][1] = (dp[i + 1][1] + (dp[i][2] + dp[i][3])) % MD; // +2jumpする. dp[i + 2][2] = (dp[i + 2][2] + (dp[i][1] + dp[i][3])) % MD; // +3jumpする. dp[i + 3][3] = (dp[i + 3][3] + (dp[i][1] + dp[i][2])) % MD; } return (dp[n][1] + dp[n][2] + dp[n][3]) % MD; }
時間計算量は,空間計算量も
改良1
- 問題制約を確認すると,は100万.
- つまり,上のプログラムでは400万×8byteをメモリに載せる.
- これを改善できないか?
上のコードをもう一度見てみると,より小さい要素には一切アクセスしないことが分かる. アクセスしない領域なら,再利用するべきでしょう.
配列の参照先をずらすんじゃなくて,配列の値をずらしましょう,という仕様です.
ll dp[4][4]; ll solve(int n) { dp[0][1] = 1; // 1jump で 1step 登る.次は 1jump は使えない. dp[1][2] = 1; // 2jump で 2step 登る.次は 2jump は使えない. dp[2][3] = 1; // 3jump で 3step 登る.次は 3jump は使えない. for (int i = 1; i < n; ++i) { // 1jumpする. dp[1][1] = (dp[1][1] + (dp[0][2] + dp[0][3])) % MD; // 2jumpする. dp[2][2] = (dp[2][2] + (dp[0][1] + dp[0][3])) % MD; // 3jumpする. dp[3][3] = (dp[0][1] + dp[0][2]) % MD; // dp配列をずらす. for (int u = 1; u <= 3; ++u) { for (int v = 1; v <= 3; ++v) { dp[u - 1][v] = dp[u][v]; } } } return (dp[0][1] + dp[0][2] + dp[0][3]) % MD; }
以上の改良により,時間計算量は,空間計算量はになりました.
改良2
- なんか解き方がフィボナッチ数列に似てるよね
現在のdp配列の状態を,次のdp配列の状態をと表現すると,上のプログラムでやってることは次のように表現できる.
どうやら,,,は要らなさそう. しかも,見るからに連立方程式なので,行列の形で書ける.
6x6行列をとすると,
- 1段目に居るときのdp配列の状態
- 2段目に居るときのdp配列の状態
- 3段目に居るときのdp配列の状態
- N段目に居るときのdp配列の状態
つまり,が計算できれば良い.
累乗計算は愚直にやると線形時間掛かってしまうが, で解く手法が編み出されている.
以上の改良により,時間計算量は,空間計算量はになりました.