Problem:
Problem G: スプリング・タイル

ある朝、あなたが目覚めると、そこはバネだらけの迷宮の中だった。

なぜ自分がこんな見知らぬ場所にいるのかはわからない。この場でじっと助けを待つという選択肢も
あるにはあるが、こういった迷宮の中に長居していると、いつかは必ず突風が吹いてきて吹き飛ばさ
れてしまうことを、あなたは経験上知っていた。しかし、突風が吹いてくるまでの時間は判断のしよ
うがない。そこであなたは、迷宮から脱出するまでに必要な移動回数の期待値が最小になるように行
動するのが最善の戦略だと考えた。

足元に落ちていた正体不明の巻物を拾ってためしに読んでみたところ、偶然にも迷宮の全体マップを
知覚することができた。さらに、たまたま持っていた草を飲むことで、すべてのワナの位置も知るこ
とができた。どうやらこの迷宮には、危険なモンスターやバネ以外のワナは存在しないらしい。

この迷宮は、いくつかの種類のタイルが長方形に並んだ形をしており、たとえば以下のようにあらわ
される。

########
#..##g.#
#*#.*#.#
#......#
#*s#*.*#
########

「.」：床。この上なら自由に歩き回ることができる。

「#」：壁。あなたは壁の中に入ることはできない。

「s」：あなたの最初の位置。このタイルの下は床になっている。

「g」：階段。このタイルの上に乗れば、迷宮を脱出したことになる。

「*」：バネ。このタイルの上に乗ると、いずれかの床の上（階段、バネのタイルは除く）にランダムに飛ばされる。それぞれの床に飛ばされる確率はすべて等しい。

タイルの種類は、この５つのいずれかである。

あなたは、現在乗っているタイルから隣り合ったタイルへ、上下左右いずれかの方向に１マスずつ移
動することができる。ただし、ナナメに移動することはできない。

迷宮のマップが与えられたとき、最善の戦略をとった場合に迷宮から脱出するまでに必要な移動回数
の期待値を求めよ。バネで飛ばされるのは移動回数には含めない。

Input

W H

c11 c12 ... c1w

c21 c22 ... c2w

::    :

ch1 ch2 ... ccw

入力の１行目には、整数W（3 ≤ W ≤ 500）と整数H（3 ≤ H ≤ 500）が、この順に空白区切りで書かれている。整数W は迷宮の横幅を、整数H は迷宮の縦幅をあらわす。

続くH 行には、迷宮のマップをあらわすW 個の文字が書かれている（空白区切りではない）。この部分の形式は、先に挙げた例のとおりである。なお、データ中に「s」と「g」は、それぞれちょうど１回ずつ出現する。また、迷宮の周囲１マスは、必ず壁で囲まれている。

なお、与えられる迷宮では、どのように移動し、バネでどのように飛ばされたとしても、「バネで飛
ばされる床を任意に設定できたとしても脱出不可能な状態」に陥ることはないと仮定してよい。

Output

最善の戦略をとったときに、脱出までにかかる移動回数の期待値を出力せよ。出力は誤差を含んでい
てもよいが、真の値との相対誤差が10-9 未満でなければならない。

Sample Input 1

8 6
########
#..##g.#
#*#.*#.#
#......#
#*s#*.*#
########

Sample Output 1

5.857142857143

Sample Input 2

21 11
#####################
#*#*.*.*.*.*.*.*.*#*#
#*#...............#*#
#*#.#.#.#.#.#.#.#.#*#
##.#.#.#.#.#.#.#.#.##
#...................#
##.#.#.#.#.#.#.#.#.##
#s#.#.#.#.#.#.#.#.#g#
#...................#
#...................#
#####################

Sample Output 2

20.000000000000

Sample Input 3

9 8
#########
#...#*.*#
#.*.#...#
#...#*.*#
#########
#.s....g#
#.*#.*..#
#########

Sample Output 3

5.000000000000

Solution in JAVA (judge verdict: Memory Limit Exceeded)

import java.math.BigDecimal;
import java.util.*;

class Point {
    public int x, y;
    public Point(int x, int y){
        this.x = x;
        this.y = y;
    }
}

class State{
    public Point p;
    public int cnt;
    State(Point p, int cnt){
        this.p = p;
        this.cnt = cnt;
    }
}


/**
 * C++???????????????????????????????????????????????????????±????
 */
public class Main {
    public static final BigDecimal EPS = new BigDecimal(1e-10);
    public static final int H = 500;
    public static final int W = 500;
    public static final int[] dy = new int[]{-1, 0, 1, 0};
    public static final int[] dx = new int[]{0, -1, 0, 1};
    public static final int scale = 16;

    int w, h;
    int[][] dis_go, dis_sp;
    BigDecimal cnt_nowall;
    Point st, go;
    List<Point> v;
    Boolean[][] wall;

    public Main(){
        Scanner sc = new Scanner(System.in);

        while(sc.hasNext()){
            w = sc.nextInt();
            h = sc.nextInt();
            v = new ArrayList<Point>();
            wall = new Boolean[h][w];

            for(int i=0;i<h;i++){
                String str = sc.next();
                for(int j=0;j<w;j++){
                    wall[i][j] = true;
                    if(str.charAt(j) == 's'){
                        st = new Point(j, i);
                        wall[i][j] = false;
                    } else if(str.charAt(j) == 'g') {
                        go = new Point(j, i);
                    } else if(str.charAt(j) == '*') {
                        v.add(new Point(j, i));
                    } else if(str.charAt(j) == '.') {
                        wall[i][j] = false;
                    }
                }
            }
            System.out.println(solve());
        }

        sc.close();
    }

    private void init_dis(){
        Queue<State> q = new LinkedList<State>();

        cnt_nowall = new BigDecimal(0);
        dis_go = new int[h][w];
        dis_sp = new int[h][w];
        for(int i=0;i<h;i++){
            for(int j=0;j<w;j++) {
                dis_go[i][j] = dis_sp[i][j] = -1;
                if(!wall[i][j]){
                    cnt_nowall = cnt_nowall.add(BigDecimal.ONE);
                }
            }
        }

        for(q.offer(new State(go, 0)); !q.isEmpty();){
            State u = q.poll();
            for(int i=0;i<4;i++){
                State u2 = new State(new Point(u.p.x+dx[i], u.p.y+dy[i]), u.cnt+1);
                if(!wall[u2.p.y][u2.p.x] && (dis_go[u2.p.y][u2.p.x] == -1 || dis_go[u2.p.y][u2.p.x] > u2.cnt)){
                    dis_go[u2.p.y][u2.p.x] = u2.cnt;
                    q.offer(u2);
                }
            }
        }

        for(Point vi : v){
            for(q.offer(new State(vi, 0)); !q.isEmpty();){
                State u = q.poll();
                for(int i=0;i<4;i++){
                    State u2 = new State(new Point(u.p.x+dx[i], u.p.y+dy[i]), u.cnt+1);
                    if(!wall[u2.p.y][u2.p.x] && (dis_sp[u2.p.y][u2.p.x] == -1 || dis_sp[u2.p.y][u2.p.x] > u2.cnt)){
                        dis_sp[u2.p.y][u2.p.x] = u2.cnt;
                        q.offer(u2);
                    }
                }
            }
        }
    }

    private BigDecimal calc_exp(int base){
        BigDecimal a = new BigDecimal(1), b = new BigDecimal(0), exp_go = new BigDecimal(0);

        for(int i=1;i<h-1;i++){
            for(int j=1;j<w-1;j++){
                if(!wall[i][j]){
                    if(dis_go[i][j] == -1 || dis_sp[i][j] != -1 && dis_go[i][j] >= dis_sp[i][j] + base){
                        a = a.subtract(BigDecimal.ONE.divide(cnt_nowall, scale, BigDecimal.ROUND_HALF_UP));
                        b = b.add(BigDecimal.valueOf(dis_sp[i][j]).divide(cnt_nowall, scale, BigDecimal.ROUND_HALF_UP));
                        //a -= 1.0 / cnt_nowall;
                        //b += (long double)dis_sp[i][j] / cnt_nowall;
                    } else {
                        exp_go = exp_go.add(BigDecimal.valueOf(dis_go[i][j]).divide(cnt_nowall, scale, BigDecimal.ROUND_HALF_UP));
                        //exp_go += (long double)dis_go[i][j] / cnt_nowall;
                    }
                }
            }
        }

        return b.add(exp_go).divide(a, scale, BigDecimal.ROUND_HALF_UP);
        //return (b + exp_go) / a;
    }

    private BigDecimal solve() {
        BigDecimal res = null;

        init_dis();

        if(dis_go[st.y][st.x] != -1) res = BigDecimal.valueOf(dis_go[st.y][st.x]);

        if(dis_sp[st.y][st.x] != -1){
            int le = 2, ri = dis_go[st.y][st.x] - dis_sp[st.y][st.x] + 1, mid1, mid2;
            BigDecimal res0, res1, res2;
            if(dis_go[st.y][st.x] == -1) ri = (h-2) * (w-2);
            while(ri - le >= 4){
                mid1 = (le * 2 + ri) / 3;
                mid2 = (le + ri * 2) / 3;
                res0 = calc_exp(le).add(BigDecimal.valueOf(dis_sp[st.y][st.x]));
                res1 = calc_exp(mid1).add(BigDecimal.valueOf(dis_sp[st.y][st.x]));
                res2 = calc_exp(mid2).add(BigDecimal.valueOf(dis_sp[st.y][st.x]));
                if(mid2 - mid1 <= 1) break;
                //if(res0 >= res1 - EPS && res1 >= res2 - EPS) le = mid1;
                if(res0.compareTo(res1.subtract(EPS)) == 1 && res1.compareTo(res2.subtract(EPS)) == 1) le = mid1;
                else ri = mid2;
            }
            for(int i=le;i<le+5;i++) {
                if(dis_go[st.y][st.x] != -1 && dis_go[st.y][st.x] < dis_sp[st.y][st.x] + i) break;
                if(res == null) res = calc_exp(i).add(BigDecimal.valueOf(dis_sp[st.y][st.x]));
                else res = res.min(calc_exp(i).add(BigDecimal.valueOf(dis_sp[st.y][st.x])));
            }
        }

        return res;
    }

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