Problem:
Score : 400 points

Problem Statement

Takahashi has a maze, which is a grid of H \times W squares with H horizontal rows and W vertical columns.

The square at the i-th row from the top and the j-th column is a "wall" square if S_{ij} is #, and a "road" square if S_{ij} is ..

From a road square, you can move to a horizontally or vertically adjacent road square.

You cannot move out of the maze, move to a wall square, or move diagonally.

Takahashi will choose a starting square and a goal square, which can be any road squares, and give the maze to Aoki.

Aoki will then travel from the starting square to the goal square, in the minimum number of moves required.

In this situation, find the maximum possible number of moves Aoki has to make.

Constraints

1 \leq H,W \leq 20

S_{ij} is . or #.

S contains at least two occurrences of ..

Any road square can be reached from any road square in zero or more moves.

Input

Input is given from Standard Input in the following format:

H W
S_{11}...S_{1W}
:
S_{H1}...S_{HW}

Output

Print the maximum possible number of moves Aoki has to make.

Sample Input 1

3 3
...
...
...

Sample Output 1

4

If Takahashi chooses the top-left square as the starting square and the bottom-right square as the goal square, Aoki has to make four moves.

Sample Input 2

3 5
...#.
.#.#.
.#...

Sample Output 2

10

If Takahashi chooses the bottom-left square as the starting square and the top-right square as the goal square, Aoki has to make ten moves.

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Wrong Answer)

import java.io.*;
import java.math.*;
import java.util.*;
import java.util.stream.*;

class Main {
    public static void main(String[] args) {
        solve();
        Stdin.flush();
    }

    static void solve() {
        int[] inputs = Stdin.receiveIntArray(" ");
        int height = inputs[0];
        int width = inputs[1];

        Grid.generate(width, height, '.');
        for (int i = 0; i < height; i++) {
            Grid.setGrid(i, Stdin.receiveCharArray());
        }

        int ans = 0;
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                Grid.bfs(Grid.point(i, j));
                int maxDist = Grid.maxDist();
                ans = ans > maxDist ? ans : maxDist;
            }
        }
        Stdin.println(ans);
    }

}

class ModCalculator {
    static final BigInteger MOD = BigInteger.valueOf((int) 1e9 + 7);
}

class Calculator { 
    static BigInteger gcd(BigInteger x, BigInteger y) {
        if (y.subtract(x).signum() > 0) {
            return gcd(y, x);
        }
        if (y.equals(BigInteger.ZERO)) {
            return x;
        }
        return gcd(y, x.remainder(y));
    }

    static BigInteger gcd(int x, int y) {
        return gcd(BigInteger.valueOf(x), BigInteger.valueOf(y));
    }

    static BigInteger gcd(BigInteger[] arr) {
        BigInteger gcd = arr[0];
        for (BigInteger i : arr) {
            gcd = gcd(gcd, i);
        }
        return gcd;
    }

    static BigInteger gcd(int[] arr) {
        BigInteger gcd = BigInteger.valueOf(arr[0]);
        for (int i : arr) {
            gcd = gcd(gcd, BigInteger.valueOf(i));
        }
        return gcd;
    }

    static BigInteger lcm(BigInteger x, BigInteger y) {
        return x.multiply(y).divide(gcd(x, y));
    }

    static BigInteger lcm(int x, int y) {
        return lcm(BigInteger.valueOf(x), BigInteger.valueOf(y));
    }

    static BigInteger lcm(BigInteger[] arr) {
        BigInteger lcm = BigInteger.ONE;
        for (BigInteger i : arr) {
            lcm = lcm(lcm, i);
        }
        return lcm;
    }

    static BigInteger lcm(int[] arr) {
        BigInteger lcm = BigInteger.ONE;
        for (int i : arr) {
            lcm = lcm(lcm, BigInteger.valueOf(i));
        }
        return lcm;
    }

    static int digits(int x) {
        return String.valueOf(x).length();
    }

    static BigInteger arraySum(BigInteger[] arr) {
        BigInteger ret = BigInteger.ZERO;
        for (BigInteger bi : arr) {
            ret = ret.add(bi);
        }
        return ret;
    }

    static BigInteger arraySum(int[] arr) {
        BigInteger ret = BigInteger.ZERO;
        for (int i : arr) {
            ret = ret.add(BigInteger.valueOf(i));
        }
        return ret;
    }
}

class Grid {
    static int width, height;
    private static char[][] grid;
    static char road;

    static void generate(int width, int height, char road) {
        Grid.width = width;
        Grid.height = height;
        Grid.road = road;
        grid = new char[height][width];
        dist = new int[height][width];
    }

    static GridPoint point(int x, int y) {
        return new GridPoint(x, y);
    }

    static char grid(int x, int y) {
        return grid[y][x];
    }

    static void setGrid(int x, int y, char point) {
        Grid.grid[y][x] = point;
    }

    static void setGrid(int y, char[] row) {
        Grid.grid[y] = row;
    }

    static void setGrid(int y, char[][] grid) {
        Grid.grid = grid;
    }

    static boolean isValidPoint(GridPoint gp) {
        return gp.x >= 0 && gp.x < width && gp.y >= 0 && gp.y < height;
    }

    static boolean isRoad(GridPoint gp) {
        return grid(gp.x, gp.y) == road;
    }

    // 最短経路
    private static int[][] dist;

    static int dist(int x, int y) {
        return dist[y][x];
    }

    static void setDist(int x, int y, int dist) {
        Grid.dist[y][x] = dist;
    }

    static int maxDist() {
        int ans = -1;
        for(int[] rowDist : dist) {
            for(int dist : rowDist) {
                ans = ans > dist ? ans : dist;
            }
        }
        return ans;
    }

    static void bfs(GridPoint initialPoint) {
        for (int i = 0; i < height; i++) {
            Arrays.fill(dist[i], -1);
        }

        Deque<GridPoint> que = new ArrayDeque<>();
        que.addLast(initialPoint);
        setDist(initialPoint.x, initialPoint.y, 0);
        while (!que.isEmpty()) {
            GridPoint point = que.remove();
            int beforeDist = dist(point.x, point.y);
            for (GridPoint.Direction dir : GridPoint.Direction.values()) {
                GridPoint next = point.move(dir);
                if (!isValidPoint(next)) {
                    continue;
                }
                if (isRoad(next)) {
                    if (dist(next.x, next.y) == -1) {
                        setDist(next.x, next.y, beforeDist + 1);
                        que.addLast(next);
                    }
                }
            }
        }
    }

    static class GridPoint {
        int x, y;

        GridPoint(int x, int y) {
            this.x = x;
            this.y = y;
        }

        GridPoint move(Direction dir) {
            return new GridPoint(x + dir.dx, y + dir.dy);
        }

        static enum Direction {
            UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0);

            int dx, dy;

            private Direction(int dx, int dy) {
                this.dx = dx;
                this.dy = dy;
            }
        }
    }
}

class Stdin {
    static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static final PrintWriter pw = new PrintWriter(System.out);

    enum Delimiter {
        NONE(""), SPACE(" "), DOT("\\."), COMMA(",");

        private String regex;

        private Delimiter(String regex) {
            this.regex = regex;
        }

        public static Delimiter of(String input) {
            try {
                return Delimiter.valueOf(input);
            } catch (IllegalArgumentException e) {
                return Arrays.stream(values()).filter(d -> d.getRegex().equals(input)).findFirst().orElse(SPACE);
            }
        }

        public String getRegex() {
            return regex;
        }
    }

    static String nextLine() {
        try {
            return br.readLine();
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
    }

    static int receiveInt() {
        return Integer.parseInt(nextLine());
    }

    static BigInteger receiveBigInteger() {
        return new BigInteger(nextLine());
    }

    static char receiveChar() {
        return nextLine().charAt(0);
    }

    static char[] receiveCharArray() {
        return nextLine().toCharArray();
    }

    static int[] receiveIntArray(Delimiter delimiter) {
        String[] inputs = nextLine().split(delimiter.getRegex());
        int[] ret = new int[inputs.length];
        for (int i = 0; i < inputs.length; i++) {
            ret[i] = Integer.parseInt(inputs[i]);
        }
        return ret;
    }

    static int[] receiveIntArray(String delimiterString) {
        return receiveIntArray(Delimiter.of(delimiterString));
    }

    static BigInteger[] receiveBigIntegerArray(Delimiter delimiter) {
        String[] inputs = nextLine().split(delimiter.getRegex());
        BigInteger[] ret = new BigInteger[inputs.length];
        for (int i = 0; i < inputs.length; i++) {
            ret[i] = new BigInteger(inputs[i]);
        }
        return ret;
    }

    static BigInteger[] receiveBigIntegerArray(String delimiterString) {
        return receiveBigIntegerArray(Delimiter.of(delimiterString));
    }

    static String[] receiveStringArray(Delimiter delimiter) {
        return nextLine().split(delimiter.getRegex());
    }

    static String[] receiveStringArray(String delimiterString) {
        return receiveStringArray(Delimiter.of(delimiterString));
    }

    static void print(Object obj) {
        pw.print(obj);
    }

    static void println(Object obj) {
        pw.println(obj);
    }

    static void println(Object... objs) {
        for (Object obj : objs) {
            println(obj);
        }
    }

    static void flush() {
        pw.flush();
    }
}