kmjp's blog

競技プログラミング参加記です

yukicoder : No.460 裏表ちわーわ

ちょっと苦戦。
http://yukicoder.me/problems/no/460

問題

H*Wのフィールドがあり、各マス0,1が埋められている。
あるマスを選択すると、そのマスと周囲8マスの01が反転する。
全マスを0にするには最小何個のマスを選択すればよいか。

解法

よくあるライツアウトに似ているが、反転する領域が異なる。
まず1行目の選択の仕方を2^W通り総当たりしよう。
以後2行目以降の選択の仕方を決めていくが、その際1つ上の行がすべて0になるような決め方をしなければならない。
また、そのような決め方が複数ある場合、選択するマス数が最小のものを選ぶ。
あとはそのように2~H行目の選択の仕方を決めていき、最終的に全マスが0になるかチェックすればよい。

前処理として、ある行の状態に対し、それらをすべて0にする最小選択マス数を先に列挙しておくと楽。

int H,W;
int A[10];
int B[10];
int mask[1<<8];
int rev[1<<8];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>H>>W;
	FOR(y,H) FOR(x,W) cin>>r, A[y] |= r<<x;
	
	FOR(i,1<<W) rev[i]=101010;
	FOR(i,1<<W) {
		FOR(x,W) if(i&(1<<x)) {
			if(x) mask[i] ^= 1<<(x-1);
			mask[i] ^= 1<<x;
			if(x<W-1) mask[i] ^= 1<<(x+1);
		}
		rev[mask[i]]=min(rev[mask[i]],__builtin_popcount(i));
	}
	
	int ma=101010;
	FOR(i,1<<W) {
		FOR(y,H) B[y]=A[y];
		B[0] ^= mask[i];
		B[1] ^= mask[i];
		int tot=__builtin_popcount(i);
		for(y=1;y<H;y++) {
			int tar=B[y-1];
			tot+=rev[tar];
			B[y-1] ^= tar;
			B[y] ^= tar;
			B[y+1] ^= tar;
		}
		
		if(B[H-1]==0) ma=min(ma,tot);
	}
	
	if(ma==101010) _P("Impossible\n");
	else _P("%d\n",ma);
	
}

まとめ

ライツアウト系問題を解いたことがあったのに妙に手間取った。