Problem 0236 : Alien Messages

解法

碁盤をグラフと見ると、求めるのはハミルトン閉路(NP完全
グラフは制限がきついので、何か上手い方法があるのかと思ったけど、思い浮かばないので無理やりメモ化探索。


パスによりグラフが二つ以上に分断されてしまうと、もう閉路を作るのは無理になるので枝刈りできる。
盤のサイズが7x7で障害物なしの場合だけひと桁局面数が多いので、そこだけアドホックにNoを出す。
後はビット演算+ハッシュなどでちまちま高速化。

ソースコード

int dy[]={-1,0,1,0},dx[]={0,-1,0,1};
int w,h,sy,sx; ll goal=0;

bool v[7][7];
void nul(int y,int x){
	v[y][x]=1;
	rep(d,4){
		int ny=y+dy[d],nx=x+dx[d];
		if(ny<0||nx<0||ny>=h||nx>=w||v[ny][nx])continue;
		nul(ny,nx);
	}
}
bool separate(ll c){
	rep(i,h)rep(j,w)v[i][j]=c&1ll<<i*w+j;
	int cnt=0;
	rep(i,h)rep(j,w)if(!v[i][j]){
		if(cnt++>0)return 1;
		nul(i,j);
	}
	return 0;
}
const int sz=1000003;
ll hash[sz];
bool ins(ll v){
	ll h=v%sz;
	for(;hash[h]!=~0ll;h=(h+1)%sz)if(hash[h]==v)return 1;
	hash[h]=v;
	return 0;
}

bool rec(int y,int x, ll c){
	if(ins(c<<6|y<<3|x))return 0;
	
	rep(d,4){
		int ny=y+dy[d],nx=x+dx[d];
		if(ny<0||nx<0||ny>=h||nx>=w||(c&1ll<<ny*w+nx))continue;
		ll nc=c|1ll<<ny*w+nx;
		
		if(nc==goal&&ny==sy&&nx==sx)return 1;
		if(!separate(nc)&&rec(ny,nx,nc))return 1;
	}
	return 0;
}
int main(){
	while(cin>>w>>h,w){
		sy=-1; sx=-1;
		ll c=0; int t;
		rep(i,h)rep(j,w){
			cin>>t;
			if(t)c^=1ll<<i*w+j;
		}
		goal=(1ll<<h*w)-1;
		
		if(h>4&&w>4&&c==0){
			if(h%2&&w%2)cout<<"No"<<endl;
			else cout<<"Yes"<<endl;
			continue;
		}
		rep(i,h)rep(j,w)if(!(c&1ll<<i*w+j)){
			int cnt=0;
			rep(d,4){
				int ny=i+dy[d],nx=j+dx[d];
				if(ny<0||nx<0||ny>=h||nx>=w||(c&1ll<<ny*w+nx))cnt++;
			}
			if(cnt>2)goto OUT;
		}
		
		rep(i,h)rep(j,w-1)if((c&1ll<<i*w+j)+(c&1ll<<i*w+j+1)==0){
			sy=i; sx=j; goto OUT;
		}
		OUT:if(sy<0){
			cout<<"No"<<endl; continue;
		}
		rep(i,sz)hash[i]=~0ll;
		cout<<(rec(sy,sx,c)?"Yes":"No")<<endl;
	}
	return 0;
}