AOJ : 0243 - Filling Game (塗りつぶしゲーム)

問題概要

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0243
問題文が日本語なので, 概要は省略です.

解法

TLEしないかどうか不安でしたが, 「深さ優先探索+ステップ数の勝手な上限予想」で解くことができました.

再帰の処理フロー

  • 盤面の色が全て同じ色である
    • それまでにかかったステップ数が解候補
  • 今見つかっている最小ステップより多く再帰している
  • 左上の色と同じ色に変えても意味がないので, それ以外の2種類の色を塗って再帰

一度の再帰における分岐は, 2つしかありません. だから間に合うのでしょう.
ちなみに, 僕は上記の枝刈りに加えて, 勝手なステップ数上限予想として, 18ステップを超えたら再帰を打ち切るようにしています(テストしている間に, 18ステップを超えることはないだろうと予想したため).

プログラム

#include <iostream>
#include <queue>
#include <cstring>
#include <climits>
using namespace std;

typedef pair<int,int> P;

int w, h, ans;
int t[12][12];
bool closed[12][12];

int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};

bool judge(int array[12][12]){
  for(int i = 0; i < h; i++){
    for(int j = 0; j < w; j++){
      if(array[i][j] != array[0][0]){
        return false;
      }
    }
  }

  return true;
}

void show(int s[12][12]){
  for(int i = 0; i < h; i++){
    for(int j = 0; j < w; j++){
      cout<<s[i][j]<<" ";
    }
    cout<<endl;
  }
  cout<<"--\n";
}

void nextState(int from[12][12], int to1[12][12], int to2[12][12]){
  int color1, color2;
  if(from[0][0] == 0)      { color1 = 1; color2 = 2; }
  else if(from[0][0] == 1) { color1 = 0; color2 = 2; }
  else                     { color1 = 0; color2 = 1; }

  for(int i = 0; i < h; i++){
    for(int j = 0; j < w; j++){
      to1[i][j] = to2[i][j] = from[i][j];
    }
  }

  queue<P> open;
  open.push(P(0, 0));
  memset(closed, 0, sizeof(closed));
  to1[0][0] = color1;
  to2[0][0] = color2;

  while(!open.empty()){
    P p = open.front();
    open.pop();

    for(int i = 0; i < 4; i++){
      int nx = p.first + dx[i];
      int ny = p.second + dy[i];

      if(0 <= nx && nx < w && 0 <= ny && ny < h &&
        !closed[ny][nx] && from[ny][nx] == from[0][0]){
        to1[ny][nx] = color1;
        to2[ny][nx] = color2;
        closed[ny][nx] = true;
        open.push(P(nx, ny));
      }
    }
  }
}

void solve(int s[12][12], int cnt){
  if(judge(s)){
    ans = min(ans, cnt);
    return;
  }
  if(cnt >= ans){
    return;
  }

  int next1[12][12];
  int next2[12][12];

  nextState(s, next1, next2);
  solve(next1, cnt + 1);
  solve(next2, cnt + 1);
}

int main(){
  while(cin >> w >> h, w || h){
    for(int i = 0; i < h; i++){
      for(int j = 0; j < w; j++){
        char ch;
        cin >> ch;

        if(ch == 'R')      t[i][j] = 0;
        else if(ch == 'G') t[i][j] = 1;
        else if(ch == 'B') t[i][j] = 2;
      }
    }

    ans = 18;
    solve(t, 0);

    cout << ans << endl;
  }
}