Problem:
Problem F: Curling Puzzle

Problem

カーリングのストーンを移動させて、ゴールのマスにストーンをぴったり重ねるパズルゲームがある。詳しいルールは次のとおりである。

フィールドはH×Wのマスからなり、はじめにいくつかのマスにストーンが置かれている。
ゴールのマスは1つだけ存在して、はじめからゴールのマスにストーンが置かれていることはない。
また、フィールドは外壁で囲まれており、途中でストーンがフィールドの外へ飛び出してしまうことはない。

プレイヤーは1手ごとにストーンを1つ選び、上下左右のいずれかの方向に打ち出して滑らせることができる。
滑っているストーンは別のストーンかフィールドの外壁にぶつかるまで滑り続ける。別のストーンにぶつかった場合は、
ぶつけられた側のストーンが反動を受け、最初に打ち出した方向と同じ方向へ連鎖的に滑り出してしまう。

例えば、図1の状態でストーンAを右方向に打ち出した場合、最終的に図2のような状態になる。

図1

図2

このパズルのクリア条件は、フィールドに存在するストーンのいずれか1つをゴールのマスにぴったり重ねることである。ゴールのマスを通り過ぎた場合にはクリアにならない。

あなたの仕事はフィールドの初期状態を入力として受け取り、そのパズルがクリア可能かどうかを出力するプログラムを作成することである。

Input

入力は以下の形式で与えられる。

H W
F11 F12 ... F1W
F21 F22 ... F2W
...
FH1 FH2 ... FHW

Fijは '.' か 'o' か '@' のいずれかであり、それぞれ以下の意味を持つ。
( 1 ≤ i ≤ H , 1 ≤ j ≤ W )

'.' : 空白のマス

'o' : カーリングのストーンが置かれているマス

'@' : ゴールのマス

Constraints

1 ≤ H ≤ 16

1 ≤ W ≤ 16

2 ≤ H × W ≤ 256

ゴールのマスが1つだけ存在することが保証されている

カーリングのストーンが置かれているマスが1つ以上存在することが保障されている

Output

パズルがクリア可能かどうか "yes" または "no" (""は含まない)を1行に出力せよ。

Sample Input 1

1 10
o........@

Sample Output 1

yes

Sample Input 2

3 6
......
.o..@.
......

Sample Output 2

no

Sample Input 3

6 4
....
.oo.
.oo.
....
.@..
....

Sample Output 3

yes

Solution in JAVA (judge verdict: Wrong Answer)

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Scanner;

public class Main {

	static Scanner sc = new Scanner(System.in);
	static final int[] DR = { 1, 0, -1, 0 };
	static final int[] DC = { 0, 1, 0, -1 };
	static int H, W, gr, gc;
	static char[][] F;

	public static void main(String[] args) {
		H = sc.nextInt();
		W = sc.nextInt();
		F = new char[H][];
		int stone = 0;
		for (int i = 0; i < H; ++i) {
			F[i] = sc.next().toCharArray();
			for (int j = 0; j < W; ++j) {
				if (F[i][j] == 'o') {
					++stone;
				} else if (F[i][j] == '@') {
					gr = i;
					gc = j;
				}
			}
		}
		boolean ans;
		if (stone >= 3) {
			if (H > 1 && W > 1) {
				ans = true;
			} else {
				ans = search();
			}
		} else if (stone == 2) {
			if (gr == 0 || gr == H - 1 || gc == 0 || gc == W - 1) {
				ans = true;
			} else {
				ans = search();
			}
		} else {
			ans = search();
		}
		System.out.println(ans ? "yes" : "no");
	}

	static boolean search() {
		HashSet<ArrayList<Point>> cur = new HashSet<>();
		ArrayList<Point> init = new ArrayList<>();
		for (int i = 0; i < H; ++i) {
			for (int j = 0; j < W; ++j) {
				if (F[i][j] == 'o') init.add(new Point(j, i));
			}
		}
		cur.add(init);
		for (int i = 0; i < 4; ++i) {
			HashSet<ArrayList<Point>> next = new HashSet<>();
			for (ArrayList<Point> st : cur) {
				for (Point p : st) {
					for (int dir = 0; dir < 4; ++dir) {
						ArrayList<Point> moved = new ArrayList<>();
						Point mp = null;
						for (Point np : st) {
							moved.add(new Point(np.x, np.y));
							if (np.equals(p)) mp = moved.get(moved.size() - 1);
						}
						while (true) {
							int nx = mp.x + DC[dir];
							int ny = mp.y + DR[dir];
							if (nx < 0 || nx >= W || ny < 0 || ny >= H) {
								if (mp.x == gc && mp.y == gr) return true;
								break;
							}
							for (Point np : moved) {
								if (np == mp) continue;
								if (np.x == nx && np.y == ny) {
									if (mp.x == gc && mp.y == gr) return true;
									mp = np;
									break;
								}
							}
							mp.x = nx;
							mp.y = ny;
						}
						//						System.out.println(moved);
						Collections.sort(moved, (Point p1, Point p2) -> {
							if (p1.x != p2.x) return Integer.compare(p1.x, p2.x);
							return Integer.compare(p1.y, p2.y);
						});
						next.add(moved);
					}
				}
			}
			cur = next;
		}
		return false;
	}

}