土下座しながら探索中

主に競技プログラミング

AOJ 0236 : Alien Messages

問題リンク : Alien Messages | Aizu Online Judge

問題概要 : 略

解法 :
枝刈り探索
まず、初期状態で次数が1のマスが存在したら No
空のマスの数を数えてその数が奇数だったら No
これらに該当しない場合はDFSで探索する
h*wのブール型の2次元配列を用意し、バックトラックで訪れたマスにtrueを入れてダメだったらfalseに戻していく
すべてのマスを1回だけ訪れた後、そのマスがスタートのマスと隣接していれば Yes
次のマスに移る前に、そこに訪れた場合ちゃんとスタートへ戻れるかをチェックする
これだけではTLEだったので、もうひとつ枝刈り
未だに訪れていないマスでスタートと次に行く場所に隣接していないマスのうち、次数が1のものが存在したら枝刈り
ここでの次数はあるマスに隣接している4つのマスのうち、未だに訪れてない空のセルの数のことです

コード:

#include<bits/stdc++.h>

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)

using namespace std;

struct Edge { int next, dir; };

int w,h,sp;
bool field[20][20];
vector<Edge> G[500];
bool visited[500],used[500];
int dx[] = {0,1,0,-1};
int dy[] = {-1,0,1,0};

bool isValid(int x,int y) { return 0 <= x && x < w && 0 <= y && y < h; }

bool check2(int cur){
  rep(i,h*w) used[i] = false;
  rep(i,h) rep(j,w) if( field[i][j] || visited[j+i*w] ) used[j+i*w] = true;
  deque<int> deq;
  deq.push_back(cur);
  used[sp] = false;
  used[cur] = true;
  while( !deq.empty() ){
    int v = deq.front(); deq.pop_front();
    if( v == sp ) return true;
    rep(i,(int)G[v].size()) {
      int next = G[v][i].next;
      if( used[next] ) continue;
      used[next] = true;
      deq.push_back(next);
    }
  }
  return false;
}


bool check3(int cur){
  int counter;
  rep(i,h) rep(j,w) if( !field[i][j] && !visited[i*w+j] ){
    counter = 0;
    rep(k,4){
      int nx = j + dx[k], ny = i + dy[k];
      if( !isValid(nx,ny) ) continue;
      if( field[ny][nx] ) continue;
      if( visited[ny*w+nx] ) continue;
      ++counter;
    }
    if( counter == 1 ) {
      int outer = 0;
      int diff_x = abs(sp%w-j), diff_y = abs(sp/w-i);
      if( diff_x+diff_y <= 1 ) ++outer;
      diff_x = abs(cur%w-j), diff_y = abs(cur/w-i);
      if( diff_x+diff_y <= 1 ) ++outer;
      if( outer == 0 ) return false;
    }
  }
  return true;
}

bool dfs(int cur,int remain){
  if( remain == 0 ) {
    int diff_x = abs( cur % w - sp % w ), diff_y = abs( cur / w - sp / w );
    if( diff_x+diff_y == 1 ) return true;
    return false;
  }
  
  rep(i,(int)G[cur].size()){
    int next = G[cur][i].next;
    if( visited[next] ) continue;
    if( !check2(next) ) continue;
    if( !check3(next) ) continue;
    visited[next] = true;
    if( dfs(next,remain-1) ) return true;
    visited[next] = false;
  }

  return false;
}

void compute(){
  sp = -1;
  rep(i,h*w) G[i].clear(), visited[i] = false;
  rep(y,h) rep(x,w) if( !field[y][x] ){
    if( sp == -1 ) sp = x + y * w;
    rep(i,4){
      int nx = x + dx[i], ny = y + dy[i];
      if( !isValid(nx,ny) ) continue;
      if( field[ny][nx]   ) continue;
      G[x+y*w].push_back((Edge){nx+ny*w,i});
    }
  }

  rep(i,h*w) if( G[i].size() == 1 ) { puts("No"); return; }
  
  int remain = 0;
  rep(i,h) rep(j,w) if( !field[i][j] ) ++remain;
  visited[sp] = true;
  if( ( remain & 1 ) || remain == 0 ) { puts("No"); return; }
  puts(dfs(sp,remain-1)?"Yes":"No");
}

int main(){
  while( cin >> w >> h, w|h ){
    rep(i,h) rep(j,w) cin >> field[i][j];
    compute();
  }
  return 0;
}