Codeforces 132 B. Piet

問題

hxwのグリッドがあり、各マスには'0'-'9'の数字が書かれている。


BP(block pointer)および、DP(direction pointer)とCP(choose pointer)という三つのポインタがある。
BPは現在のブロックを指している。
DPは現在の進む方向を指している。上下左右のいずれかである。
CPは次に曲がる方向を示している。DPに対して右に90度または左に90度のどちらかである。


n回、次の動作を繰り返す。

  • 次のブロックが、現在のBPのブロックと同じ色の間、BPはDPの向きへ動き続ける。
  • 次のブロックが、現在のBPのブロックと同じ色の間、BPはCPの向きへ動き続ける。
  • 動けなくなったら、BPからみてDPの向きの隣のマスを見る。
  • それが色つきのマス('1'-'9')ならBPをそのマスにする。
  • 黒('0')のマスあるいはグリッドの外だったら、
    • CPがDPから見て左に90度だったら、右に90度にする
    • CPがDPから見て右に90度だったら、DPを時計回りに90度回転させ、CPをDPから見て左に90度にする

全ての動作を終えたあとにBPが指しているブロックの色を答えよ。

制約条件

h,w≦50
n≦5*10^7

方針

シミュレートする。
BP,DP,CPが一つに定まると、次のBP,DP,CPは一つに定まるので、
あらかじめ全て計算しておく(あるいはメモ化する)

ソースコード

const int dy[]={-1,0,1,0},dx[]={0,1,0,-1};
int h,w,n;
string in[50];
pair<pi,pi> memo[50][50][4][2];
pair<pi,pi> next(int y,int x,int dp,int cp){
	if(memo[y][x][dp][cp].first.first>=0)return memo[y][x][dp][cp];
	int cy=y, cx=x, cdp=dp, ccp=cp, d=cp?dp+1&3:dp+3&3;
	for(;;){
		int ny=cy+dy[dp], nx=cx+dx[dp];
		if(ny<0||nx<0||nx>=w||ny>=h||in[ny][nx]!=in[cy][cx])break;
		cy=ny; cx=nx;
	}
	for(;;){
		int ny=cy+dy[d], nx=cx+dx[d];
		if(ny<0||nx<0||nx>=w||ny>=h||in[ny][nx]!=in[cy][cx])break;
		cy=ny; cx=nx;
	}
	int ny=cy+dy[dp], nx=cx+dx[dp];
	if(ny<0||nx<0||nx>=w||ny>=h||in[ny][nx]=='0'){
		if(cp)ccp=0, cdp=dp+1&3;
		else ccp=1;
		
		return memo[y][x][dp][cp]=mp(mp(cy,cx),mp(cdp,ccp));
	}
	return memo[y][x][dp][cp]=mp(mp(ny,nx),mp(dp,cp));
}

void run()
{
	cin>>h>>n;
	rep(i,h)cin>>in[i];
	w=in[0].size();
	memset(memo,-1,sizeof(memo));
	int y=0,x=0,dp=1,cp=0;
	rep(i,n){
		pair<pi,pi> nxt=next(y,x,dp,cp);
		y=nxt.first.first; x=nxt.first.second;
		dp=nxt.second.first; cp=nxt.second.second;
	}
	cout<<in[y][x]<<endl;
}