SRM 209 DIV1 Hard CaseysArt
問題
□□ □
という形のブロックが無限にある。ブロックは自由に(90度単位で)回転できる。
length*widthの長方形にぴったり詰め込みたい(空白の部分があってはいけない)。
その「場合の数」を答えよ。ただし、対称の形でも別に数える
- 1 <= length <= 18
- 1 <= width <= 15
- 返り値はdouble型。1e-9の相対誤差を含んでも良い
解答
一列覚えるDP。(y座標, x座標, 前(width+1)個の状態)
そのままではメモリが足りないのでy座標の部分を2つだけ覚えるようにする
コメント
書いたものが一発で動いてびっくりした。
しかしメモリが足りなかったので普通の再帰から書きなおした。でもコピペしてyの部分を&1してループを逆順からやるだけなので、簡単だった。
この形には慣れることが出来たといえるだろう。
コード
#include <vector> #include <cmath> #include <ctime> #include <cstring> #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define mset(m,v) memset(m,v,sizeof(m)) using namespace std; typedef long double ld; ld dp[2][15][1<<16]; //long doubleの意味は無いと思う struct CaseysArt { int h, w; double howManyWays(int length, int width) { h = length, w = width; mset(dp, -1); for(int i = h-1; i >= 0; i --) for(int j = w-1; j >= 0; j --) rep(mask, 1<<(w+1)) { ld r = 0; int nmask = mask >> 1; int ni = j+1 == w ? i+1 : i, nj = j+1 == w ? 0 : j+1; if(mask&1) { if(i == h-1 && j == w-1) { r = nmask == 0; }else { r += dp[ni&1][nj][nmask]; } }else { if(i+1 < h && j+1 < w && (~nmask&(1<<(w-1)))) r += dp[ni&1][nj][nmask | (1 << (w-1)) | (1 << w)]; if(i+1 < h && j-1 >=0 && (~nmask&(1<<(w-2))) && (~nmask&(1<<(w-1)))) r += dp[ni&1][nj][nmask | (1 << (w-2)) | (1 << (w-1))]; if(j+1 < w && ~mask&2) { int nmask2 = mask >> 2; int ni2 = j+2 == w ? i+1 : i, nj2 = j+2 == w ? 0 : j+2; if(i+1 < h && (~nmask2&(1<<(w-2)))) r += dp[ni2&1][nj2][nmask2 | (1 << (w-2))]; if(i+1 < h && (~nmask2&(1<<(w-1)))) r += dp[ni2&1][nj2][nmask2 | (1 << (w-1))]; } } dp[i&1][j][mask] = r; } return dp[0][0][0]; } };