Problem:
Problem D: Dr. Nakamura's Lab.

Dr.中村は偉大な発明者である, 今日も常人には発想出来ない新たな発明に取り掛かっている. 今発明しているのは新たな防犯システムとコンテナである.

防犯システムの方は上に乗った者を識別して招かれざる客だった場合, 電流を流して気絶させるパネルである. このパネルは踏まないように上を通過しても無駄で, 空中のものに対し放電する. しかし, まだ完成には程遠く, 電流は強すぎて調節が不可能で, 一度放電すると充電しない限り使えない. また, 人と物体の区別ができず, そもそも人自体区別できない等欠点をあげればキリがない.

コンテナの方は謎の力によって常時地面から浮いており, 運ぶのに便利に作ってある. しかし, これも完成には遠く, 一度押すと何かにぶつかるまで止まらない.

Dr.中村は一日の作業を終え, 帰宅しようとしたが未完成のパネルを設置したままだということに気づいた. これを取り外すのは防犯上非常に困難に作ってある上に, これを通らなければ帰れないような位置に設置してしまったのでDr.中村は途方に暮れた. しかし, 素晴らしい頭脳を持つDr.中村は解決案をすぐに思いついた. パネルは一度放電させれば通れるようになるため, コンテナを通過させればパネルの上を通れるようになる, そう考えた. 実際, パネルの上にコンテナを通過させると, コンテナは電流によって消滅してしまう一方でパネルは放電し, 通れるようになる.

しかし, 研究室の中は入り組んでおり, うまくコンテナを動かさないとパネルを通過させられない. Dr.中村は助手であるあなたにメールで助けを求めた. あなたの仕事は研究室の情報が与えられた時にDr.中村が出口までたどり着くためのプログラムを作成することである.

研究室は二次元グリッドで与えられ, Dr.中村は一度の移動で上下左右に隣接するセルに移動することが出来る.ただし, 障害物, コンテナ, 放電していないパネルのセルには移動できない.

Dr.中村は上下左右に隣接するセルにあるコンテナを押すことができ, コンテナはDr.中村がいる方向と逆の方向に別のコンテナか障害物にぶつかる, あるいは放電されていないパネルの上を通過するまで動く. また, 何かにぶつかり止まった場合, ぶつかる直前のセルで静止する.

出口はコンテナもDr.中村も侵入可能である. コンテナは出口を通っても消滅せず、そのまま通過する.

壁にぶつかって出口をふさぐことはある. この場合は出口に進入できない.

あなたのプログラムが要求されていることは, Dr.中村は最少何回の移動で研究室を脱出できるかを出力することのみである. なぜなら, Dr.中村は素晴らしい頭脳の持ち主なのでそれさえわかれば自分で脱出するからである.

Input

入力は複数のデータセットからなり, 一行目に研究室の縦の長さ H と横の長さ W が与えられる.
2行目以降では研究室の状態が H × W 個の文字で表わされる. 各文字が表すものは以下のとおりである:

‘#’障害物を表す.

‘@’Dr.中村の初期位置を表す.

‘w’パネルを表す.

‘c’コンテナを表す.

‘.’何も置かれていない空白のセルを表す.

‘E’脱出口を表す.

縦と横の長さがともに 0 のとき入力の終了を表す. これについて処理をする必要はない.

以下のことを仮定してよい.

3 ≤ H, W ≤ 10

研究室の周囲は障害物で囲まれている.

パネルとコンテナの数はそれぞれ 3 を越えない.

研究室にはただ１つの出口がある.

Output

各データセットについて最少の移動回数を１行に出力せよ. なお、Dr.中村が脱出できない場合、”-1”と出力せよ.

Sample Input

5 5
#####
##@##
#wc.#
#Ew.#
#####
5 5
#####
##@.#
#wc.#
#E#.#
#####
3 6
######
#@.wE#
######
0 0

Output for the Sample Input

3
5
-1

Solution in JAVA (judge verdict: Accepted)

import java.util.*;

public class Main {
  // constant

  static int MAX_INT = Integer.MAX_VALUE;

  // global variables

  static HashMap<Integer, Integer> dists;

  // inner classes

  static class Stat {
    final int id;
    final int[] cids;
    final boolean[] ps;
    final int hc;

    Stat(int id, int[] cids, boolean[] ps) {
      this.id = id;
      this.cids = cids.clone();
      this.ps = ps.clone();
      this.hc =
	(id << 24) |
	(cids[0] << 17) | (cids[1] << 10) | (cids[2] << 3) |
	(ps[0] ? 4 : 0) | (ps[1] ? 2 : 0) | (ps[2] ? 1 : 0);
    }

    @Override
    public int hashCode() {
      return hc;
    }

    @Override
    public String toString() {
      return
	"[ " + id + " [ " + cids[0] + " " + cids[1] + " " + cids[2] +
	" ] [ " + ps[0] + " " + ps[1] + " " + ps[2] + " ] ] (" +
	hc + ") = " + dists.get(hc);
    }
  }

  static class StatComp implements Comparator<Stat> {
    public int compare(Stat st0, Stat st1) {
      return (dists.get(st0.hc).intValue() - dists.get(st1.hc).intValue());
    }
  }

  // subroutines

  static int findArray(int[] arr, int key) {
    for (int i = 0; i < arr.length; i++)
      if (arr[i] == key) return i;
    return -1;
  }

  static void setDist(Stat sn, PriorityQueue<Stat> q, int d) {
    if (dists.containsKey(sn.hc)) {
      if (dists.get(sn.hc) > d) {
	//System.out.println("(" + sn.hc + ") " + dists.get(sn.hc) + "->" + d);
	dists.put(sn.hc, d);
	q.remove(sn);
	q.add(sn);
      }
    }
    else {
      //System.out.println("(" + sn.hc + ") MAX_INT->" + d);
      dists.put(sn.hc, d);
      q.add(sn);
    }
  }

  // main
  public static final void main(String[] args) throws Exception {
    Scanner sc = new Scanner(System.in);

    for (;;) {
      int h = sc.nextInt();
      int w = sc.nextInt();
      if (h == 0 && w == 0) break;

      String[] lines = new String[h];
      for (int y = 0; y < h; y++)
	lines[y] = sc.next();

      int nc = 0, np = 0;
      int[] cids = {0, 0, 0};
      int[] pids = {0, 0, 0};
      boolean[] ps = {false, false, false};

      int sid = 0, gid = 0;

      int[] grds = new int[h * w];
      Arrays.fill(grds, 0);

      for (int y = 0; y < h; y++)
	for (int x = 0; x < w; x++) {
	  int id = y * w + x;

	  switch (lines[y].charAt(x)) {
	  case '#':
	    grds[id] = -1;
	    break;
	  case '@':
	    sid = id;
	    break;
	  case 'w':
	    pids[np] = id;
	    ps[np] = true;
	    np++;
	    grds[id] = np;
	    break;
	  case 'c':
	    cids[nc] = id;
	    nc++;
	    break;
	  case 'E':
	    gid = id;
	    break;
	  }
	}
      //System.out.printf("sid = %d, gid = %d\n", sid, gid);

      Stat stn = new Stat(sid, cids, ps);

      dists = new HashMap<Integer, Integer>();
      dists.put(stn.hc, 0);

      int min_dist = -1;
      int[] dids = {1, -w, -1, w};

      PriorityQueue<Stat> q = new PriorityQueue<Stat>(100, new StatComp());
      q.add(stn);

      while (! q.isEmpty()) {
	Stat un = q.poll();
	//System.out.println(un);

	int uid = un.id;
	int[] ucids = un.cids;
	boolean[] ups = un.ps;

	if (uid == gid) {
	  min_dist = dists.get(un.hc);
	  break;
	}

	int ud = dists.get(un.hc);

	for (int did: dids) {
	  int vid = uid + did;
	  int gv = grds[vid];
	  if (gv < 0 || (gv > 0 && ups[gv - 1])) continue;

	  int ci = findArray(ucids, vid);
	  // System.out.printf("did=%d, ci=%d\n", did, ci);

	  if (ci >= 0) {
	    int[] vcids = ucids.clone();
	    boolean[] vps = ups.clone();

	    while (grds[vcids[ci] + did] >= 0 && 
		   findArray(vcids, vcids[ci] + did) < 0) {
	      vcids[ci] += did;
	      if (grds[vcids[ci]] > 0) {
		int pi = grds[vcids[ci]] - 1;
		if (vps[pi]) {
		  vps[pi] = false;
		  vcids[ci] = 0;
		  break;
		}
	      }
	    }

	    //System.out.printf("vcids[ci]=%d, ucids[ci]=%d\n",
	    //vcids[ci], ucids[ci]);
	    if (vcids[ci] != ucids[ci]) {
	      Stat vn = new Stat(uid, vcids, vps);
	      setDist(vn, q, ud);
	    }

	    continue;
	  }

	  Stat vn = new Stat(vid, ucids, ups);
	  setDist(vn, q, ud + 1);
	}
      }

      System.out.println(min_dist);
    }
  }
}