2157 Maze

問題概要

縦M横Nのグリッドで表わされる迷路がある。
各グリッドにおいて.は何もないマスを表わし、Xは壁を表わし、a〜eは扉A〜Eの鍵をあらわす。
スタートとゴールはそれぞれS,Gの文字であらわされる。


迷路において、扉を開けるためには、対応する小文字の鍵を全て持っていなければならない。スタートからゴールへの経路があるかを判定せよ。

解法

歩数などは求めなくてよいので、以下のような幅優先探索で解ける。

  • 鍵を全て持っている扉のマスに辿り着いたら、それはキューに入れる。
  • 鍵を全て拾い終えたら、対応する扉のうち、周囲4マスにすでに行ったマスがあるものをキューに入れる。

2番目の操作がないと、

S..a
AXXX
G...

たぶんこのような迷路でNOを出力してしまう。

ソースコード

int dy[]={-1,0,1,0},dx[]={0,-1,0,1};
int sy,sx,h,w,ky[5];
bool v[20][20]; int tk[5];
char in[20][20];

int main(){
	while(scanf("%d%d ",&h,&w),h){
		rep(i,5)ky[i]=tk[i]=0;
		rep(i,h){
			gets(in[i]);
			rep(j,w){
				char c=in[i][j];
				if('a'<=c&&c<='e')ky[c-'a']++;
				if(c=='S')sy=i,sx=j;
				v[i][j]=0;
			}
		}
		queue<pair<int,int> > Q; Q.push(mp(sy,sx));
		while(!Q.empty()){
			int y=Q.front().first,x=Q.front().second; Q.pop();
			if(v[y][x])continue; v[y][x]=1;
			
			if(in[y][x]=='G'){
				puts("YES"); goto END;
			}
			
			if('a'<=in[y][x]&&in[y][x]<='e'){
				char c=in[y][x];
				if(++tk[c-'a']==ky[c-'a']){
					rep(i,h)rep(j,w)if(in[i][j]==c-'a'+'A'){
						rep(d,4){
							int ny=i+dy[d],nx=j+dx[d];
							if(ny<0||nx<0||ny>=h||nx>=w)continue;
							if(v[ny][nx])Q.push(mp(i,j));
						}
					}
				}
			}
			rep(d,4){
				int ny=y+dy[d],nx=x+dx[d];
				if(ny<0||nx<0||ny>=h||nx>=w||v[ny][nx]||in[ny][nx]=='X')continue;
				if('A'<=in[ny][nx]&&in[ny][nx]<='E'&&tk[in[ny][nx]-'A']<ky[in[ny][nx]-'A'])continue;
				Q.push(mp(ny,nx));
			}
		}
		puts("NO"); END:;
	}
	return 0;
}