Problem:
Score : 400 points

Problem Statement

A maze is composed of a grid of H \times W squares - H vertical, W horizontal.

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

There is a magician in (C_h,C_w). He can do the following two kinds of moves:

Move A: Walk to a road square that is vertically or horizontally adjacent to the square he is currently in.

Move B: Use magic to warp himself to a road square in the 5\times 5 area centered at the square he is currently in.

In either case, he cannot go out of the maze.

At least how many times does he need to use the magic to reach (D_h, D_w)?

Constraints

1 \leq H,W \leq 10^3

1 \leq C_h,D_h \leq H

1 \leq C_w,D_w \leq W

S_{ij} is # or ..

S_{C_h C_w} and S_{D_h D_w} are ..

(C_h,C_w) \neq (D_h,D_w)

Input

Input is given from Standard Input in the following format:

H W
C_h C_w
D_h D_w
S_{11}\ldots S_{1W}
\vdots
S_{H1}\ldots S_{HW}

Output

Print the minimum number of times the magician needs to use the magic. If he cannot reach (D_h,D_w), print -1 instead.

Sample Input 1

4 4
1 1
4 4
..#.
..#.
.#..
.#..

Sample Output 1

1

For example, by walking to (2,2) and then using the magic to travel to (4,4), just one use of magic is enough.

Note that he cannot walk diagonally.

Sample Input 2

4 4
1 4
4 1
.##.
####
####
.##.

Sample Output 2

-1

He cannot move from there.

Sample Input 3

4 4
2 2
3 3
....
....
....
....

Sample Output 3

0

No use of magic is needed.

Sample Input 4

4 5
1 2
2 5
#.###
####.
#..##
#..##

Sample Output 4

2

Solution in C# (.NET Core 3.1.201) (judge verdict: Wrong Answer)

//
// ABC176D
// URL : https://atcoder.jp/contests/abc176/tasks/abc176_d
// 
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Runtime.InteropServices.ComTypes;
using System.Collections.Specialized;
using System.Net.Http.Headers;
using System.Data.SqlTypes;

namespace AtCoder.Template.ABC176D
{
    class Solver
    {
        public static readonly long MOD_CONST = 1000000007;//(long)Math.Pow(10, 9) + 7;

        public static void Main(string[] args)
        {
            //出力高速化
            var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
            Console.SetOut(sw);
            var solver = new Solver();
            solver.Solve();
            Console.Out.Flush();
        }
        public static int[] dx = new int[4] { 0, 0, -1, +1 };
        public static int[] dy = new int[4] { -1, +1, 0, 0 };
        public static int[] dwx = new int[24] { -2, -2, -2, -2,-2,
            -1, -1, -1, -1, -1,
            0,0,0,0,
            1,1,1,1,1,
            2,2,2,2,2 };
        public static int[] dwy = new int[24] { -2, -1, 0, 1, 2,
            -2, -1, 0, 1, 2,
            -2, -1,  1, 2,
            -2, -1, 0, 1, 2,
            -2, -1, 0, 1, 2 };
        static int H, W;
        static int Ch, Cw;
        static int Dh, Dw;
        static char[,] S;

        static readonly int INVALID_ID = -1;
        static readonly int UNKNWON_AREA_ID = 0;
        static readonly char WALL = '#';
        static readonly char ROAD = '.';
        public void Solve()
        {
            int[] HW = Console.ReadLine().Split(' ').Select(_inputStr => int.Parse(_inputStr)).ToArray();
            H = HW[0];
            W = HW[1];
            //魔法使いのマス
            int[] Chw = Console.ReadLine().Split(' ').Select(_inputStr => int.Parse(_inputStr)).ToArray();
            Ch = Chw[0]-1;
            Cw = Chw[1]-1;
            //ゴール
            int[] Dhw = Console.ReadLine().Split(' ').Select(_inputStr => int.Parse(_inputStr)).ToArray();
            Dh = Dhw[0]-1;
            Dw = Dhw[1]-1;

            S = new char[H,W];
            for(int h = 0; h < H; ++h)
            {
                string s = Console.ReadLine();
                for(int w = 0; w < W; ++w){
                    S[h, w] = s[w];
                }
            }
            Console.WriteLine(findAns());
        }




        public int findAns()
        {
            //スタートから開始して、ゴールを目指す
            var memo = new int[H, W];
            for (int h = 0; h < H; ++h)
            {
                for (int w = 0; w < W; ++w)
                {
                    memo[h, w] = int.MaxValue;
                }
            }
            var paintPoints = new List<int[]>();
            int sy = Ch;
            int sx = Cw;
            bfs(sy, sx, 0, ref memo, out paintPoints);
            for (int cost = 0; ; ++cost)
            {
                //コスト0でたどり着けるマスから、移動Bで行けて、まだコスト0以下でたどり着けないマスを列挙
                List<int[]> candidates = new List<int[]>();
                search(cost, paintPoints, ref memo, out candidates);

                if(candidates.Count == 0) { break; }
                int nextCost = cost + 1;
                foreach(var next in candidates)
                {
                    int y = next[0];
                    int x = next[1];
                    //もう探索済みならスキップ
                    if(memo[y,x] <= nextCost)
                    {
                        continue;
                    }
                    bfs(y, x, nextCost, ref memo, out paintPoints);
                }
            }
            var goalCost = memo[Dh, Dw];
            return goalCost == int.MaxValue ? -1 : goalCost;
        }
        public static void bfs(int sy, int sx, int cost,ref int[,] costs, out List<int[]> paintPoints)
        {
            paintPoints = new List<int[]>();
            //今いる場所を初期化
            costs[sy, sx] = cost;
            paintPoints.Add(new int[2] { sy, sx });

            var nextPosQueue = new Queue<int[]>();
            nextPosQueue.Enqueue(new int[2] { sy, sx });
            while(nextPosQueue.Count > 0)
            {
                var next = nextPosQueue.Dequeue();
                int ny = next[0];
                int nx = next[1];
                for (int i = 0; i< 4; ++i)
                {
                    //移動先
                    int x = nx + dx[i];
                    int y = ny + dy[i];
                    if (x < 0 || W <= x) { continue; }
                    if (y < 0 || H <= y) { continue; }
                    //移動先が壁
                    if(S[y,x] == WALL) { continue; }
                    //もう行っているならパス
                    if(costs[y,x] <= cost) { continue; }
                    //まだ行ってないところ
                    costs[y, x] = cost;
                    var nextPoint = new int[2] { y, x };
                    paintPoints.Add(nextPoint);
                    nextPosQueue.Enqueue(nextPoint);
                }
            }
        }
        public static void search(int cost, List<int[]> paintPoints, ref int[,] costs, out List<int[]> candidates)
        {
            candidates = new List<int[]>();
            foreach(var point in paintPoints)
            {
                int h = point[0];
                int w = point[1];
                //移動Bで行ける場所をリストアップ
                for (int i = 0; i < 24; ++i)
                {
                    int x = w + dwx[i];
                    int y = h + dwy[i];
                    //移動不可能なところならスキップ
                    if (x < 0 || W <= x) { continue; }
                    if (y < 0 || H <= y) { continue; }
                    if (S[y, x] == WALL) { continue; }
                    //次の場所に行くのにかかるコストがcostより大きい場所なら
                    //(例 : まだ調べてない場所)
                    //もう調べているなら、コストはcost以下のはず
                    if (costs[y, x] > cost)
                    {
                        //移動Bで行けて調べる場所の候補に加える
                        candidates.Add(new int[2] { y, x });
                    }
                }
            }
            return;
        }


        //---------------------------------------------------------------------------------
        #region MyLib
        /// <summary>
        /// string -> List<char>に分解する。1文字がListの1要素
        /// </summary>
        /// <param name="a"></param>
        /// <returns></returns>
        public static List<char> separeteToList(string a)
        {
            var l = new List<char>(a.Length);
            foreach (char x in a)
            {
                l.Add(x);
            }
            return l;
        }
        /// <summary>
        /// List<char> -> stringに合成する
        /// </summary>
        /// <param name="l"></param>
        /// <returns></returns>
        public static string combineCharList(List<char> l)
        {
            var ans = new StringBuilder();
            foreach (char x in l)
            {
                ans.Append(x);
            }
            return ans.ToString();
        }

        /// <summary>
        /// Aの文字をなるべくCorrectに合うように並びかえる。当然Correctにしかない文字もあるので、その場合はAの適当な文字で置き換える
        /// 例)
        /// A       = abcde, eppstein
        /// Corrent = acfbe, pipeline
        /// ans     = acdbe, pspetine
        /// </summary>
        /// <param name="A"></param>
        /// <param name="Correct"></param>
        public static List<char> FillChar(List<char> A, List<char> Correct)
        {
            char Empty = ' ';
            var ARemain = new List<char>(A);
            var ans = new List<char>(A.Count);
            for (int i = 0; i < Correct.Count; ++i)
            {
                //Aの文字をなるべくCorrectに合うように、ansの文字を決める
                int foundPos = ARemain.FindIndex(x => x == Correct[i]);
                if (foundPos != -1)//文字がCorrectの中で見つかったら
                {
                    char found = ARemain[foundPos];
                    ans.Add(found);
                    ARemain.Remove(found);//使った文字は除外
                }
                else//Aにない文字もある。後で使わなかったAの適当な文字で埋める
                {
                    ans.Add(Empty);
                }
            }
            //Aにない文字がCorrectにあったら、Aの残りの文字で適当に埋める
            for (int i = 0; i < A.Count; ++i)
            {
                if (ans[i] == Empty)
                {
                    ans[i] = ARemain.First();
                    ARemain.RemoveAt(0);
                }
            }
            return ans;
        }


        /// <summary>
        /// リストから、重複したものを除外したリストを新規に作成する
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="src"></param>
        /// <param name="dst"></param>
        public static void getDistinctList<T>(List<T> src, out List<T> dst)
        {
            var iter = src.Distinct();
            dst = new List<T>();
            foreach (var item in iter)
            {
                dst.Add(item);
            }
        }

        /// <summary>
        /// グラフ系
        /// </summary>
        public class Node
        {
            public Node(long val){
                this.Value = val;
                this.Children = new List<Node>();
            }

            public Node(long val, List<Node> children)
            {
                this.Value = val;
                this.Children = children;
            }

            public long Value { get; set; }
            public List<Node> Children { get; set; }
        }

        /// <summary>
        /// Clamping
        /// </summary>
        /// <typeparam name="T">比較型</typeparam>
        /// <param name="x">clampしたい値</param>
        /// <param name="min">最小値</param>
        /// <param name="max">最大値</param>
        /// <returns>clamp済みの値</returns>
        public static T Clamp<T>(T x, T min, T max) where T : IComparable<T>
        {
            if (x.CompareTo(min) < 0) { return min; }
            else if (x.CompareTo(max) > 0) { return max; }
            else return x;
            //return Math.Max(Math.Min(x, max), min);
        }

        /// <summary>
        /// 指定された底log_baseでの, 対数を計算
        /// </summary>
        /// <param name="x"></param>
        /// <param name="log_base">指定の底</param>
        /// <returns></returns>
        public static double LogBase(double x, double log_base)
        {
            //log_b(N) = log_a(N) / log_a(B)より
            return Math.Log(x) / Math.Log(log_base);
        }

        /// <summary>
        /// 初項a, 公差d, 項数nの数列の和
        /// </summary>
        /// <param name="n">求めたいところ</param>
        /// <returns>初項からxまで足した値</returns>
        static long SumSeries(long a, long d, long n)
        {
            return (2 * a + (n - 1) * d) * n / 2;
        }
        /// <summary>
        /// 累積和の配列を計算する
        /// Sum[ a[0], a[i] )がs[i+1]に入っている
        /// s[0]  : 0                //一切和を取らない場合
        /// s[1]  : ls[0]            //a[0]   + s[0] = a[0]
        /// s[2]  : ls[1] + s[1]     //a[1]   + a[0]
        /// ...
        /// s[N]  : ls[N-1] + s[N-1] //a[N-1] + ... + a[0]
        /// s[N+1]: ls[N] + s[N]     //a[N]   + ... + a[0]
        /// 
        /// 例: [1, 7)の和(a[1] + ... + a[6])
        /// s[1] = a[0] //[0, 1)
        /// s[7] = a[0] + a[1] + ... + a[6] //[0, 7)
        /// よってs[7] - s[1]);
        /// </summary>
        /// <param name="ls">計算したい配列</param>
        /// <returns>累積和配列</returns>
        static List<long> AggregateSumList(List<long> a)
        {
            var s = new List<long>(a.Count + 1);
            if (a.Count == 0)
            {
                return s;
            }
            s.Add(0);//s[0] = 0
            for (int i = 0; i < a.Count; ++i)
            {
                s.Add(a[i] + s[i]);
            }
            return s;
        }


        /// <summary>
        /// 与えられた数が素数かどうかを計算する
        /// </summary>
        /// <param name="num">対象数</param>
        /// <returns>true:素数, false:素数ではない(合成数)</returns>
        public static bool isPrime(int num)
        {
            if (num < 2) return false;
            else if (num == 2) return true;
            else if (num % 2 == 0) return false;

            double sqrtNum = Math.Sqrt(num);
            for (int i = 3; i <= sqrtNum; i += 2)
            {
                if (num % i == 0)
                {
                    return false;
                }
            }
            return true;
        }
        /// <summary>
        /// 素因数分解
        /// </summary>
        /// <param name="n"></param>
        /// <returns>keyが素数、valueがその素数の登場回数</returns>
        public static Dictionary<long, long> PrimeFactorization(long n)
        {
            var result = new Dictionary<long, long>();
            //初めの素数2から割っていく
            for (long p = 2; p * p <= n; p++)
            {
                //割り切れないなら、素因数に含まれてない
                if (n % p != 0) continue;
                long count = 0;

                // 割り続ける
                while (n % p == 0)
                {
                    count++;
                    n /= p;
                }

                result[p] = count;
            }

            // 素数が残っている場合
            if (n != 1) result[n] = 1;
            return result;
        }


        /// <summary>
        /// 順列mPnを計算する
        /// </summary>
        /// <param name="m"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public static long Permutation(long m, long n)
        {
            long ret = 1;
            for (var i = 0; i < n; ++i)
            {
                ret *= m - i;
            }
            return ret;
        }
        /// <summary>
        /// 組み合わせmCnを計算する
        /// </summary>
        /// <param name="m"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public static long Combination(long m, long n)
        {
            if (m == n || n == 0)
            {
                return 1;
            }
            long ans = 1;
            for (var i = 1; i <= n; ++i)
            {
                ans = ans * (m - i + 1) / i;
            }
            return ans;
        }

        static long Gcd(long m, long n)
        {
            if (n == 0) return m;
            return Gcd(n, m % n);
        }

        static long Lcm(long m, long n)
        {
            var gcd = Gcd(m, n);
            //return (m * n) / gcd;
            return m / gcd * n;//桁あふれ対策
        }
        #endregion MyLib
    }
}