AOJ-1308: Awkward Lights

keyword

ライツアウト 掃き出し法 Java

問題概要

W*H(W,H<25)のライツアウトの初期盤面が与えられる。ただしこのライツアウトは、押したマスとそこからマンハッタン距離dのマスの状態が変わる。全てoffにすることができるかどうか判定する問題。

解法

マンハッタン距離云々は本質に全く関係ない。連立一次方程式をmod 2の世界で解くだけ。計算量はO( (W*H)^3 )。計算量は係数に1/6だか1/3だかがつくけど割と厳しい。でも問題なく通った。

感想

本番中に解けなかったことを一番悔いていた問題。

import java.util.*;

public class Main {

	int[][] board;
	int d, M, N, S;
	int[][] matrix;

	void run(){
		Scanner in = new Scanner(System.in);
		for(;;){
			M = in.nextInt();
			N = in.nextInt();
			d = in.nextInt();
			if(M==0 && N==0 && d==0) return ;
			board = new int[N][M];
			for(int i=0; i<N; i++){
				for(int j=0; j<M; j++){
					board[i][j] = in.nextInt();
				}
			}
			System.out.println(solve()?1:0);
		}
	}

	void setMatrix(){
		S = M*N;
		matrix = new int[S][S+1];
		for(int i=0; i<N; i++){
			for(int j=0; j<M; j++){
				if(board[i][j] == 1){
					matrix[i*M+j][S] = 1;
				}
			}
		}

		for(int i=0; i<N; i++){
			for(int j=0; j<M; j++){
				matrix[i*M+j][i*M+j] = 1;
				for(int k=0; k<=d; k++){
					for(int l=0; l<4; l++){
						int ny = i + ((l&1)==1?k:-k),
							nx = j + (((l>>1)&1)==1?d-k:k-d);
						if(0<=ny && ny<N && 0<=nx && nx<M){
							matrix[ny*M+nx][i*M+j] = 1;
						}
					}
				}
			}
		}

	}

	boolean solve(){
		setMatrix();
		for(int i=0; i<S; i++){
			boolean found = false;
			int key = -1;
			for(int k=i; k<S; k++){
				if(matrix[k][i] == 1){
					found = true;
					key = k;
					break;
				}
			}
			if(found){
				int[] tmp = matrix[i].clone();
				matrix[i] = matrix[key].clone();
				matrix[key] = tmp;
				for(int k=i+1; k<S; k++)if(matrix[k][i] == 1){
					for(int j=i; j<=S; j++){
						matrix[k][j] ^= matrix[i][j];
					}
				}
			}
		}
		
		for(int i=S-1; i>=0; i--){
			if(matrix[i][i] == 1){
				for(int k=i-1; k>=0; k--)if(matrix[k][i] == 1){
					for(int j=S; j>=i; j--){
						matrix[k][j] ^= matrix[i][j];
					}
				}
			}
		}
		
		for(int i=0; i<S; i++){
			if(matrix[i][i] == 0 && matrix[i][S] == 1){
				return false;
			}
		}
		return true;
	}

	public static void main(String args[]){
		new Main().run();
	}
}