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 C# (Mono 4.6.2.0) (judge verdict: Wrong Answer)

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using static System.Math;

namespace CSharpSpiral {
	class Program {
		static void Main(string[] args) {
			var sw = new StreamWriter(Console.OpenStandardOutput()) {
				AutoFlush=
#if DEBUG
			true
#else
			false
#endif
			};
			Console.SetOut(sw);
			new Program().Solve(new ConsoleInput(
#if DEBUG
			new StreamReader("../../../inputData.txt")
#else
			Console.In
#endif
			, ' '));
			Console.Out.Flush();
		}
		public void Solve(ConsoleInput cin) {
			var H = cin.ReadInt;
			var W = cin.ReadInt;
			var S = new bool[H, W];
			var G = new Graph(H*W);
			var firstH = 0;
			var firstW = 0;
			var landFound = false;
			for(int i = 0; i<H; i++) {
				var line = cin.ReadLine;
				for(int j = 0; j<W; j++) {
					S[i, j]=line[j]=='.';
					if(!landFound&&S[i, j]) {
						firstH=i; firstW=j;
						landFound=true;
					}
				}
			}
			for(int i = 0; i<H; i++) {
				for(int j = 0; j<W-1; j++) {
					if(S[i, j]&&S[i, j+1]) {
						G.AddEdge(i*W+j, i*W+j+1, 1);
						G.AddEdge(i*W+j+1, i*W+j, 1);
					}
				}
			}
			for(int i = 0; i<H-1; i++) {
				for(int j = 0; j<W; j++) {
					if(S[i, j]&&S[i+1, j]) {
						G.AddEdge(i*W+j, (i+1)*W+j, 1);
						G.AddEdge((i+1)*W+j, i*W+j, 1);
					}
				}
			}
			int[] tree;
			var IJK = G.Dijkstra(firstH*W+firstW, out tree);
			var M = IJK.Max();
			var farest = IJK.ToList().IndexOf(M);
			var Res = G.Dijkstra(farest, out tree).Max();
			Console.WriteLine(Res);
		}
	}
	static class ExtendClass {
		public static string Join<T>(this IEnumerable<T> X, string separator = " ") => string.Join(separator, X);
		public static void MergeSort<T>(this T[] A, bool desending) where T : IComparable<T> {
			if(desending)
				MergeSort(A, Comparer<T>.Create((x, y) => y.CompareTo(x)));
			else
				MergeSort(A, Comparer<T>.Create((x, y) => x.CompareTo(y)));
		}
		public static void MergeSort<T>(T[] A, IComparer<T> comparer) where T : IComparable<T> {
			MergeSort1(0, A.Length, A, comparer);
		}
		private static void Merge<T>(int left, int mid, int right, T[] A, IComparer<T> comparer)
			where T : IComparable<T> {
			var nl = mid-left;
			var nr = right-mid;
			var L = new T[nl];
			var R = new T[nr];
			for(int i = 0; i<nl; i++)
				L[i]=A[i+left];
			for(int i = 0; i<nr; i++)
				R[i]=A[i+mid];
			int il = 0, ir = 0;
			for(int k = left; k<right; k++) {
				if(comparer.Compare(L[il], R[ir])<=0) {
					A[k]=L[il++];
					if(il==nl) {
						k++;
						for(; ir<nr;)
							A[k++]=R[ir++];
						break;
					}
				} else {
					A[k]=R[ir++];
					if(ir==nr) {
						k++;
						for(; il<nl;)
							A[k++]=L[il++];
						break;
					}
				}
			}
		}
		private static void MergeSort1<T>(int left, int right, T[] A, IComparer<T> comparer)
		where T : IComparable<T> {
			if(left+1<right) {
				var mid = (left+right)/2;
				MergeSort1(left, mid, A, comparer);
				MergeSort1(mid, right, A, comparer);
				Merge(left, mid, right, A, comparer);
			}
		}
	}
	class ConsoleInput {
		private readonly TextReader _stream;
		private readonly char _separator = ' ';
		private readonly Queue<string> inputStream;
		public ConsoleInput(TextReader stream, char separator = ' ') {
			this._separator=separator;
			this._stream=stream;
			inputStream=new Queue<string>();
		}
		public string Read {
			get {
				if(inputStream.Count!=0) return inputStream.Dequeue();
				string[] tmp = _stream.ReadLine().Split(_separator);
				for(int i = 0; i<tmp.Length; ++i)
					inputStream.Enqueue(tmp[i]);
				return inputStream.Dequeue();
			}
		}
		public string ReadLine { get { return _stream.ReadLine(); } }
		public int ReadInt { get { return int.Parse(Read); } }
		public long ReadLong { get { return long.Parse(Read); } }
		public double ReadDouble { get { return double.Parse(Read); } }
		public string[] ReadStrArray(long N) {
			var ret = new string[N];
			for(long i = 0; i<N; ++i)
				ret[i]=Read;
			return ret;
		}
		public int[] ReadIntArray(long N) { var ret = new int[N]; for(long i = 0; i<N; ++i) ret[i]=ReadInt; return ret; }
		public long[] ReadLongArray(long N) { var ret = new long[N]; for(long i = 0; i<N; ++i) ret[i]=ReadLong; return ret; }
		public double[] ReadDoubleArray(long N) { var ret = new double[N]; for(long i = 0; i<N; ++i) ret[i]=ReadDouble; return ret; }
		public bool isEOF {
			get { return _stream.Peek()==-1; }
		}
	}
	class PriorityQueue<T> where T : IComparable<T> {
		protected readonly List<T> body = new List<T>();
		protected Comparer<T> comparer { get; }
		public PriorityQueue(bool maxTop = true) {
			comparer
			=maxTop ? Comparer<T>.Create((T i, T j) => i.CompareTo(j))
			: Comparer<T>.Create((T i, T j) => j.CompareTo(i));
		}
		public PriorityQueue(Comparer<T> mComparer) { comparer=mComparer; }
		//suppose Me>Her if isRootMaximum
		protected int Compare(T Me, T Her) => comparer.Compare(Me, Her);
		protected int Compare(int x, int y) => Compare(body[x], body[y]);
		protected virtual void Swap(int x, int y) { T tmp = body[x]; body[x]=body[y]; body[y]=tmp; }
		public virtual void Push(T elem) {
			body.Add(elem);
			//the parent of n-th element is the ((n+1)>>1)-1-th
			UpHeap(lastIndex);
		}
		public void Push(IEnumerable<T> seq) {
			foreach(var item in seq) {
				Push(item);
			}
		}
		protected int UpHeap(int j) {
			while(j>0) {
				int parent = ((j+1)>>1)-1;
				if(Compare(parent, j)<0) {
					Swap(parent, j);
				} else break;
				j=parent;
			}
			return j;
		}
		public virtual T Pop() {
			T top = body[0];
			Swap(0, lastIndex);
			body.RemoveAt(lastIndex);
			DownHeap(0);
			return top;
		}
		protected int DownHeap(int j) {
			while(j<length) {
				var leftChild = ((j+1)<<1)-1;
				if(leftChild>=length) break;
				var toSwap = leftChild;
				var rightChild = leftChild+1;
				if(rightChild<length&&Compare(leftChild, rightChild)<0)
					toSwap=rightChild;
				if(Compare(j, toSwap)<0) {
					Swap(toSwap, j);
				} else break;
				j=toSwap;
			}
			return j;
		}
		public T Top { get { return body[0]; } }
		public int length { get { return body.Count(); } }
		protected int lastIndex { get { return body.Count()-1; } }
		public bool Any() => body.Any();
		public string Emit() { return body.ToArray().Join(","); }
	}
	class ImmutablePriorityQueue<T>: PriorityQueue<T> where T : IComparable<T> {
		public Dictionary<T, int> map { get; private set; } = new Dictionary<T, int>();
		public ImmutablePriorityQueue(bool maxTop) : base(maxTop) { }
		public ImmutablePriorityQueue(Comparer<T> mComparer) : base(mComparer) { }
		protected override void Swap(int x, int y) {
			map[body[x]]=y;
			map[body[y]]=x;
			base.Swap(x, y);
		}
		public override void Push(T elem) {
			map.Add(elem, length);
			base.Push(elem);
		}
		public override T Pop() {
			var V = base.Pop();
			map.Remove(V);
			return V;
		}
		public void Remove(T X) {
			var x = map[X];
			map.Remove(X);
			Swap(x, lastIndex);
			body.RemoveAt(lastIndex);
			var b = DownHeap(x);
			if(b==x) UpHeap(x);
		}
		public void Update(T X) {
			var x = map[X];
			var b = DownHeap(x);
			if(b==x) UpHeap(x);
		}
		public bool hasKey(T X) { int c; return map.TryGetValue(X, out c); }
	}
	class Graph: Graph<long> {
		public Graph(int _N) : base(_N) { }
		public long?[] Dijkstra(int start, out int[] froms) {
			var tmp = new long?[N];
			froms=new int[N];
			var PQComparer = Comparer<int>.Create(
				(i, j) => {
					if(tmp[i].HasValue) {
						return tmp[j].HasValue ? -Nullable.Compare(tmp[i], tmp[j]) : 1;
					} else {
						return tmp[j].HasValue ? -1 : 0;
					}
				}
				);
			//top is min of the valids, null is smaller than others
			var PQ = new ImmutablePriorityQueue<int>(PQComparer);//manages ids of nodes
			var Picked = new bool[N];
			tmp[start]=0;
			for(int i = 0; i<N; i++)
				froms[i]=-1;
			froms[start]=-1;
			for(int i = 0; i<N; i++)
				PQ.Push(i);
			while(PQ.Any()) {
				var v = PQ.Pop();
				Picked[v]=true;
				//TODO
				foreach(var wc in Edges[v]) {
					var w = wc.Key;
					if(Picked[w]) continue;
					var c = wc.Value;
					if(tmp[w]==null||tmp[w]>tmp[v]+c) {
						tmp[w]=tmp[v]+c;
						PQ.Update(w);
						froms[w]=v;
					}
				}
			}
			return tmp;
		}
	}
	class Graph<T> {
		public readonly int N;
		public T[] Nodes { get; }
		protected List<int>[] Nexts { get; }
		protected Dictionary<int, T>[] Edges { get; set; }
		public Graph(int _N) {
			N=_N;
			Nodes=new T[_N];
			Nexts=new List<int>[_N];
			Edges=new Dictionary<int, T>[_N];
			for(int i = 0; i<_N; i++) {
				Nexts[i]=new List<int>();
				Edges[i]=new Dictionary<int, T>();
			}
		}
		public void Update(int i, T value) {
			Nodes[i]=value;
		}
		public void AddEdge(int from, int to, T value) {
			Edges[from].Add(to, value);
			Nexts[from].Add(to);
		}
		private void PointDFS(int from, Action<int> onEnter, Action<int> onLeave) {
			var S = new Stack<int>();
			S.Push(from);
			var entered = new bool[N];
			var departed = new bool[N];//dep<=ent
			var childCount = new int[N];
			while(S.Any()) {
				var v = S.Peek();
				if(!entered[v]) {
					onEnter?.Invoke(v);
					entered[v]=true;
				}
				for(int i = childCount[v]; i<Nexts[v].Count; i++) {
					if(!entered[Nexts[v][i]]) {
						S.Push(Nexts[v][i]);
						childCount[v]=i+1;
						goto NextLoop;
					}
				}
				S.Pop();
				childCount[v]=Nexts[v].Count;
				if(!departed[v]) {
					onLeave?.Invoke(v);
					departed[v]=true;
				}
			NextLoop:;
			}
		}
	}
}