SRM523 Div1 500
問題
1*1*1〜1*1*kのレゴブロックが無数にある。
土台が1*1*wのところに重ねていき、高さをhにしたい(土台は高さに含めない)。
条件として、ブロックを重ねるとき、重ねるブロックからはみ出ないようにする必要がある。
そのような重ね方の組み合わせは全部で何通りあるか?MOD1000000007で答える。
考え方
見るからにDP。
使えそうな式として、
dp[残りの高さi][土台の幅j] := 積み上げ方の数
があれば、dp[i][j]はdp[i-1][?]から求められそう。
単純に、土台の幅をどうブロックを組み合わせて置くかを試すのは、組み合わせが多すぎて×。
ここでDPで、i-1までdpが計算されているとして、幅xのブロックを置いたあと、1空マス、残り、という風にして、
[ x ][ ][ 残り ]
xの部分は、幅xのブロックの組み合わせ*dp[i-1][x]になる。
残りの部分は、dp[i][残り]になる。
この残りの部分は計算できて、jの部分を0,1,...,wと順番に計算すると考えると、
[ x ][ ][ 残り ]全体の長さがjのとき、残りの部分はdp[i][j-x-1]で、j-x-1はjより小さく、すでに計算済みである。
結局、dp[i][j]は、上のようなパターンで計算できるので、
dp[i][j] = {幅xのブロックの組み合わせ * dp[i-1][x] *dp[i][j-x-1]}の合計値 + 幅wのブロックの組み合わせ * dp[i-1][w]
の計算になる。
(全部に置かないパターンは、xが0の時に内包されている)
計算の都合上、dp[0][]はすべて1(通り)。
次に、「幅xのブロックの組み合わせ」を考える。
使えるブロックの幅は最大でもkまでであることに注意する。(k=wなら2^{x-1}になる?)
これも同じようなDPで、
[ 0〜kまでのブロック ][ 残り ]
と考えることができて、
dp[i] := {幅xのブロックを置く * dp[i-x]}の合計値
になる。「幅xのブロックを置く」は1通りしかないので、結局、
dp[i] := dp[i-x]の合計値(xは1からkまで)
で求まる。
これを前計算しておいて、上の計算をすれば、dp[h][w]が答えになる。
反省
- 典型的なDPっぽい
- 前部分xを押さえて、残りは計算済み、みたいな?
- 複数回(再帰的)のDP適用を考える