PKU : 1020 - Anniversary Cake

問題概要

http://poj.org/problem?id=1020

正方形の形をしたs*sのケーキを, a1*a1, a2*a2,...,an*an のN個のケーキに分割できるかどうかを答えてください.

アルゴリズム

深さ優先探索で枝刈です.
各深さにおいては, N個の正方形の内, どれで切り取っていくかを決定します.
切り取る位置は, 左上からループで見ていって, 一番最初に見つかった切り取れる場所で切り取ります.

プログラム

int s,n;
int t[20];
bool flg[100][100],used[20];

//[y][x]〜[y+len-1][x+len-1]のケーキを切り取れるかどうかの判定
bool check(int x,int y,int len){
  if(x + len > s || y + len > s) return false;

  REP(i,y,y+len){
    REP(j,x,x+len){
      if(flg[i][j]){
        return false;
      }
    }
  }
  return true;
}

//[y][x]〜[y+len][x+len]にvalを書き込む
void draw(int x,int y,int len,bool val){
  REP(i,y,y+len){
    REP(j,x,x+len){
      flg[i][j] = val;
    }
  }
}

//ケーキ切り取りDFS
//戻り値:true-全て切り取れる, false-切り取れない
//rem:あといくつ切り取らないといけないか
bool dfs(int rem){
  if(rem == 0) return true;

  //左上から見ていって, 空いている場所を探す(ここが切り取る位置になる)
  int sx,sy;
  for(sx=0;sx<s;sx++){
    for(sy=0;sy<s;sy++){
      if(!flg[sy][sx]){
        break;
      }
    }
    if(sy != s) break;
  }

  int before = -1;
  rep(i,n){
    //すでに切り取った or すでに探索した値 or (sx,sy)からti*tiは無理
    if(used[i] || before == t[i] || !check(sx,sy,t[i])) continue;

    //書き込み
    draw(sx,sy,t[i],true);
    used[i] = true;
    if(dfs(rem-1)) return true;
    used[i] = false;
    draw(sx,sy,t[i],false);

    before = t[i];
  }

  return false;
}

int main(){
  int T;
  scanf("%d",&T);

  while(T--){
    scanf("%d%d",&s,&n);

    int sum = 0;
    rep(i,n){
      scanf("%d",&t[i]);
      sum += t[i] * t[i];
    }
    if(sum != s*s){
      printf("HUTUTU!\n");
      continue;
    }

    sort(t,t+n,greater<int>());
    memset(flg,0,sizeof(flg));
    memset(used,0,sizeof(used));
    bool ans = dfs(n);
    printf("%s\n", ans ? "KHOOOOB!" : "HUTUTU!");
  }
}