PKU : 2663 - Tri Tiling

問題概要

http://poj.org/problem?id=2663
3*nの枠に, 1*2 or 2*1のタイルを使って, ぴったり埋める方法は何通りあるでしょう.
ただし, n=0のときは, 答えは1です.

アルゴリズム

メモ化再帰で解きました.
他の人のブログを見てみると, すごくかしこい方針で解いているのに対して, 僕の方針はあまりかしこくないかもです.


[3][n]の枠があるとき, 以下のように左上から順番に単純再帰して考えていきました.
[0][0]→[1][0]→[2][0]→[0][1]→...


あとは, メモ化する方法です.
[i][j]以降のパターン数を求めるためには, j列目とj+1列目の状態(6つの場所がタイルで埋まっているか否か)がわかってさえいれば, 確定させることができます.
なので, 次のようなメモ用配列を作ってメモ化しました.

  • dp[3][31][2^6]


他の人は, O(n)で解けてるみたいなので, そっちの方法を検索してもらった方がいいかもです.

プログラム

int n;
int dp[4][32][64];
bool t[4][32];

int dfs(int x,int y){
  //インデックスが下に飛び出したら右上へ補正
  if(y == 3){
    x++;
    y = 0;
  }

  //右端までいけば再帰終了
  if(x == n){
    if(t[0][n-1] && t[1][n-1] && t[2][n-1]) return 1;
    return 0;
  }

  //x列目とx+1列目の状態をビット化する
  int bit = 0;
  rep(i,3){
    REP(j,x,x+2){
      bit <<= 1;
      bit += t[i][j];
    }
  }
  if(dp[y][x][bit] != -1) return dp[y][x][bit];

  int res = 0;

  //今の場所がタイルで埋まっているとき
  if(t[y][x]){
    res = dfs(x,y+1);
  }
  //今の場所がタイルで埋まっていないとき
  else{
    //横置き
    if(x < n-1){
      t[y][x] = t[y][x+1] = true;
      res += dfs(x,y+1);
      t[y][x] = t[y][x+1] = false;
    }
    //縦置き
    if(y < 2 && !t[y+1][x]){
      t[y][x] = t[y+1][x] = true;
      res += dfs(x,y+1);
      t[y][x] = t[y+1][x] = false;
    }
  }

  return dp[y][x][bit] = res;
}

int main(void){
  while(cin>>n,n!=-1){
    if(n == 0){
      cout<<"1\n";
      continue;
    }
    if(n % 2 == 1){
      cout<<"0\n";
      continue;
    }

    memset(t,0,sizeof(t));
    memset(dp,-1,sizeof(dp));
    cout<<dfs(0,0)<<endl;
  }
}