Problem:
イルミネーション (Illumination)

問題

JOI 社の建物は図のような 1 辺 1 メートルの正六角形をつなぎ合わせた形である．クリスマスが近づいているので，JOI 社では建物の壁面をイルミネーションで飾り付けることにした．ただし，外から見えない部分にイルミネーションを施すのは無駄なので，イルミネーションは外から建物の中を通らずに行くことのできる壁面にのみ飾り付けることにした．

図: JOI 社の建物の配置の例

上の図は上空から見た JOI 社の建物の配置の例である．正六角形内の数字は座標を表す．灰色の正六角形は建物がある場所を表し，白色の正六角形は建物がない場所を表す．この例では，赤の実線で示される部分がイルミネーションで飾り付けを行う壁面となり，その壁面の長さの合計は 64 メートルとなる．

JOI 社の建物の配置を表す地図が与えられたとき，飾り付けを行う壁面の長さの合計を求めるプログラムを作成せよ．ただし，地図の外側は自由に行き来できるものとし，隣接した建物の間は通ることはできないものとする．

入力

入力ファイルの 1 行目には 2 つの整数 W, H (1 ≦ W ≦ 100，1 ≦ H ≦ 100) が空白を区切りとして書かれている．続く H 行には JOI 社の建物の配置が書かれている．i + 1 行目 (1 ≦ i ≦ H) には W 個の整数が空白を区切りとして書かれており，j 個目 (1 ≦ j ≦ W) の整数は座標 (j, i) の正六角形に建物がある時は 1 であり，ない時は 0 である．また，与えられる入力データには建物が必ず 1 つ以上ある．

地図は以下の規則によって記述されている．

地図の最も北の行の最も西の正六角形は座標 (1, 1) である．

座標 (x, y) の正六角形に隣接する東隣の正六角形は座標 (x + 1, y) である．

y が奇数の時，座標 (x, y) の正六角形に隣接する南西の正六角形の座標は (x, y + 1) である．

y が偶数の時，座標 (x, y) の正六角形に隣接する南東の正六角形の座標は (x, y + 1) である．

出力

イルミネーションで飾り付けを行う壁面の長さの合計を 1 行で出力せよ．

入出力例

入力例 1

8 4
0 1 0 1 0 1 1 1
0 1 1 0 0 1 0 0
1 0 1 0 1 1 1 1
0 1 1 0 1 0 1 0

出力例 1

64

入出力例 1 は問題文中の例に対応しており，イルミネーションで飾り付けを行う壁面の長さの合計は 64 メートルである．

入力例 2

8 5
0 1 1 1 0 1 1 1
0 1 0 0 1 1 0 0
1 0 0 1 1 1 1 1
0 1 0 1 1 0 1 0
0 1 1 0 1 1 0 0

出力例 2

56

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in JAVA (judge verdict: Accepted)

import java.awt.Point;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ
		new Main().mainrun();
	}

	private Scanner scan;
	private int W,H,ans,count;

	private int[][] F;
	private int[][]	vec_a = {{-1,-1},{-1,0},{0,1},{1,0},{1,-1},{0,-1}},	//偶数列
						vec_b = {{-1,0},{-1,1},{0,1},{1,1},{1,0},{0,-1}};	//奇数列

	private int[][] state;

	private HashMap<Integer,Boolean> stateMap;

	private Deque<Point> deque;

	private void mainrun() {

		scan = new Scanner(System.in);

		W = scan.nextInt();
		H = scan.nextInt();
		count = 0;

		deque = new LinkedList<>();

		stateMap = new HashMap<>();
		stateMap.put(0, false);

		F = new int[H + 2][W + 2];
		state = new int[H][W];
		ans = 0;

		Arrays.fill(F[0], 0);
		Arrays.fill(F[H + 1], 0);

		for(int i = 1;i < H + 1 ;i++) {
			F[i][0] = 0;
			F[i][W + 1] = 0;
			for(int j = 1;j < W + 1;j++) {
				F[i][j] = scan.nextInt();

				if(F[i][j] == 1) {
					ans++;
				}
			}
			Arrays.fill(state[i-1], 0);
		}

		for(int i = 1; i < H + 1;i++) {
			for(int j = 1;j < W + 1;j++) {
				if(F[i][j] == 0 && state[i-1][j-1] == 0) {
					count++;
					stateMap.put(count, true);

					state[i-1][j-1] = count;

					deque.add(new Point(j,i));
					Point p;

					while(!deque.isEmpty()) {
						p = deque.remove();
						putState(p.y,p.x);
					}
				}
			}
		}

		for(int i = 1; i < H + 1;i++) {
			for(int j = 1;j < W + 1;j++) {
				if(F[i][j] == 0 && stateMap.get(state[i-1][j-1])) {
					F[i][j] = 1;
					ans++;
				}
			}
		}

		ans *= 6;

		for(int i = 1; i < H + 1 ;i++) {
			for(int j = 1;j < W + 1;j++) {
				if(F[i][j] == 1) {
					ans -= aroundNum(i,j);
				}
			}
		}

		System.out.println(ans);

		scan.close();
	}

	private void putState(int y,int x) {
		int xx,yy;
		int[][] vec;

		if(y % 2 ==0) {
			vec = vec_a;
		}else {
			vec = vec_b;
		}

		for(int i = 0;i < 6;i++) {
			xx = x + vec[i][1];
			yy = y + vec[i][0];

			if(xx == 0 || yy == 0|| xx == W + 1 || yy == H + 1) {//範囲外
				stateMap.put(count, false);
				continue;
			}

			if(F[yy][xx] == 1|| state[yy-1][xx-1] != 0) {
				continue;
			}

			state[yy-1][xx-1] = count;
			deque.add(new Point(xx,yy));
		}
	}

	private int aroundNum(int y,int x) {
		int cnt = 0,xx,yy;
		int[][] vec;
		if(y % 2 == 0) {
			vec = vec_a;
		}else {
			vec = vec_b;
		}
		for(int i = 0;i < 6; i++) {
			xx = x + vec[i][1];
			yy = y + vec[i][0];
			if(F[yy][xx] == 1) {
				cnt++;
			}
		}
		return cnt;
	}
}