using System;
using System.Collections.Generic;
using System.Linq;

namespace _LuckyPermutation
{
    public class Puzzle
    {
        private static readonly List<int> Lucky = GetLuckyNumbers(7);

        static List<int> GetLuckyNumbers(int length)
        {
            var strNums = new List<string> { "4", "7" };
            var prevCount = 0;
            while (strNums[strNums.Count - 1].Length < length)
            {
                var count = strNums.Count;
                for (var i = prevCount; i < count; i++)
                {
                    strNums.Add(strNums[i] + "4");
                    strNums.Add(strNums[i] + "7");
                }
                prevCount = count;
            }

            return strNums.Select(int.Parse).ToList();
        }

        private static readonly long[] Factorials =
            new[] { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800 };

        public static int[] FindPerm(int n, int k, out int nseq)
        {
            nseq = n;   // number of non-sequential elements
            if (k < 1 || (n < Factorials.Count() && k > Factorials[n]) || k > 1000000000) return null;
            var perm = new HashSet<int> { 0 };

            if (n > 13)
            {
                nseq = 0;
                for (var i=1; i<Factorials.Count(); i++)
                {
                    if (k < Factorials[i] || k >= Factorials[i + 1]) continue;
                    nseq = i + 1;
                    break;
                }
                for (var i=1; i<=n-nseq; i++)
                {
                    perm.Add(i);
                }

            }

            var pos = 1L;
            for (var i = nseq; i > 0; i--)
            {
                var f = (i > Factorials.Count()) ? Factorials[Factorials.Count() - 1] : Factorials[i - 1];
                for (var j = (n - nseq + 1); j <= n; j++)
                {
                    if (perm.Contains(j)) continue;

                    if (pos + f > k)
                    {
                        perm.Add(j);
                        break;
                    }
                    pos += f;
                }
            }

            return perm.ToArray();
        }

        public static int CountLucky(int[] seq, int nseq)
        {
            if (seq == null) return -1;

            var count = 0;
            var seqCount = seq.Count() - nseq;

            var i = 0;
            while (Lucky[i++] <= seqCount) count++;

            for (var j = seqCount + 1; j < seq.Count(); j++)
            {
                if (Lucky.Contains(j) && Lucky.Contains(seq[j]))
                {
                    count++;
                }
                //Console.Write("{0},", seq[j]);
            }
            //Console.WriteLine(count);
            //Console.WriteLine();

            return count;
        }
    }

    class ProgramLuckyPermutation
    {
        static void Main(/*string[] args*/)
        {
            var result = Console.ReadLine();
            if (result == null) return;
            var strnk = result.Split();
            var n = Convert.ToInt32(strnk[0]);  // permutation of integers between 1 and n
            var k = Convert.ToInt32(strnk[1]);  // lexicographical number of the permutation

            int nseq;
            var seq = Puzzle.FindPerm(n, k, out nseq);
            Console.WriteLine(Puzzle.CountLucky(seq, nseq));
        }
    }
}


using System;
using CompLib.Util;

public class Program
{
    private int N;
    private int[,] A;

    private int[] X, Y;

    private int[][] Dx;
    private int[][] Dy;

    private Tuple<int, int>[,,,] Dp;

    private bool[,,,] F;

    public void Solve()
    {
        var sc = new Scanner();
        N = sc.NextInt();
        A = new int[N, N];
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                A[i, j] = sc.NextInt() - 1;
            }
        }

        X = new int[N * N];
        Y = new int[N * N];
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                X[A[i, j]] = i;
                Y[A[i, j]] = j;
            }
        }

        {
            Dx = new int[3][];
            Dy = new int[3][];

            Dx[0] = new int[] {-2, -2, -1, -1, 1, 1, 2, 2};
            Dy[0] = new int[] {-1, 1, -2, 2, -2, 2, -1, 1};

            Dx[1] = new int[36];
            Dy[1] = new int[36];

            Dx[2] = new int[36];
            Dy[2] = new int[36];
            for (int i = 0; i < 9; i++)
            {
                int ii = i + 1;
                Dx[1][i] = ii;
                Dy[1][i] = ii;

                Dx[1][i + 9] = ii;
                Dy[1][i + 9] = -ii;

                Dx[1][i + 18] = -ii;
                Dy[1][i + 18] = ii;

                Dx[1][i + 27] = -ii;
                Dy[1][i + 27] = -ii;

                Dx[2][i] = ii;
                Dx[2][i + 9] = -ii;

                Dy[2][i + 18] = ii;
                Dy[2][i + 27] = -ii;
            }
        }

        // 現在地、ターゲット、駒
        Dp = new Tuple<int, int>[N, N, N * N, 3];
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                for (int k = 0; k < N * N; k++)
                {
                    for (int l = 0; l < 3; l++)
                    {
                        Dp[i, j, k, l] = new Tuple<int, int>(int.MaxValue, -1);
                    }
                }
            }
        }

        F = new bool[N, N, N * N, 3];

        int d = int.MaxValue;
        int c = -1;
        for (int i = 0; i < 3; i++)
        {
            var t = Go(X[0], Y[0], 1, i);
            if (t.d < d || (t.d == d && t.c < c))
            {
                d = t.d;
                c = t.c;
            }
        }

        Console.WriteLine($"{d} {c}");
    }

    (int d, int c) Go(int x, int y, int t, int k)
    {
        if (t == N * N) return (0, 0);
        if (F[x, y, t, k]) return (int.MaxValue, -1);
        if (Dp[x, y, t, k].Item1 != int.MaxValue) return (Dp[x, y, t, k].Item1, Dp[x, y, t, k].Item2);

        int d = int.MaxValue;
        int c = -1;
        F[x, y, t, k] = true;
        // 移動
        for (int i = 0; i < Dx[k].Length; i++)
        {
            int nx = x + Dx[k][i];
            int ny = y + Dy[k][i];
            if (nx < 0 || nx >= N) continue;
            if (ny < 0 || ny >= N) continue;
            int nt = X[t] == nx && Y[t] == ny ? t + 1 : t;
            var tmp = Go(nx, ny, nt, k);
            if (tmp.d == int.MaxValue) continue;
            if (tmp.d + 1 < d || (tmp.d + 1 == d && tmp.c < c))
            {
                d = tmp.d + 1;
                c = tmp.c;
            }
        }

        // 駒交換
        for (int i = 0; i < 3; i++)
        {
            if (i == k) continue;
            var tmp = Go(x, y, t, i);
            if (tmp.d == int.MaxValue) continue;
            if (tmp.d + 1 < d || (tmp.d + 1 == d && tmp.c + 1 < c))
            {
                d = tmp.d + 1;
                c = tmp.c + 1;
            }
        }

        F[x, y, t, k] = false;
        Dp[x, y, t, k] = new Tuple<int, int>(d, c);
        return (d, c);
    }

    public static void Main(string[] args) => new Program().Solve();
}

namespace CompLib.Util
{
    using System;
    using System.Linq;

    class Scanner
    {
        private string[] _line;
        private int _index;
        private const char Separator = ' ';

        public Scanner()
        {
            _line = new string[0];
            _index = 0;
        }

        public string Next()
        {
            if (_index >= _line.Length)
            {
                string s;
                do
                {
                    s = Console.ReadLine();
                } while (s.Length == 0);

                _line = s.Split(Separator);
                _index = 0;
            }

            return _line[_index++];
        }

        public string ReadLine()
        {
            _index = _line.Length;
            return Console.ReadLine();
        }

        public int NextInt() => int.Parse(Next());
        public long NextLong() => long.Parse(Next());
        public double NextDouble() => double.Parse(Next());
        public decimal NextDecimal() => decimal.Parse(Next());
        public char NextChar() => Next()[0];
        public char[] NextCharArray() => Next().ToCharArray();

        public string[] Array()
        {
            string s = Console.ReadLine();
            _line = s.Length == 0 ? new string[0] : s.Split(Separator);
            _index = _line.Length;
            return _line;
        }

        public int[] IntArray() => Array().Select(int.Parse).ToArray();
        public long[] LongArray() => Array().Select(long.Parse).ToArray();
        public double[] DoubleArray() => Array().Select(double.Parse).ToArray();
        public decimal[] DecimalArray() => Array().Select(decimal.Parse).ToArray();
    }
}


using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleTidbits
{
    class JustCodeForces
    {
        int N;
        int[,] board;
        Dictionary<int, Tuple<int, int>> locations;

        public static void Main()
        {
            JustCodeForces obj = new JustCodeForces();

            //while (true)
                obj.ThreePieces_ContestAnalysis();
        }

        int M;
        Tuple<int, int>[][] dist;
        Tuple<int, int>[] pos;

        int[] X = new int[] { 2, 2, -2, -2, 1, 1, -1, -1 };
        int[] Y = new int[] { -1, 1, -1, 1, 2, -2, 2, -2 };
        private bool In(int x, int y)
        {
            if (x >= 0 && y >= 0 && x < N && y < N) return true;
            return false;
        }

        private int Get(int x, int y, int p)
        {
            return x * 3 * N + y * 3 + p;
        }
        private void ThreePieces_ContestAnalysis()
        {
            N = int.Parse(Console.ReadLine());

            pos = new Tuple<int, int>[N * N];
            M = (N - 1) * N * 3 + N * 3 + 3;
            dist = new Tuple<int, int>[M][];
            for (int i = 0; i < M; i++)
                dist[i] = new Tuple<int, int>[M];

            for (int i = 0; i < N; i++)
            {
                var array = Array.ConvertAll(Console.ReadLine().Split(new char[] { ' ' }), int.Parse);

                for (int j = 0; j < N; j++)
                {
                    pos[--array[j]] = Tuple.Create(i, j);
                }
            }

            for (int i = 0; i < M; i++)
            {
                for (int j = 0; j < M; j++)
                {
                    dist[i][j] = Tuple.Create(100000, 100000);
                    if (i == j) dist[i][j] = Tuple.Create(0, 0);
                }
            }

            for (int i = 0; i < N; i++)
            {
                for (int j = 0; j < N; j++)
                {
                    //Get Knights distance
                    for (int k = 0; k < 8; k++)
                    {
                        int nx = i + X[k];
                        int ny = j + Y[k];

                        if (In(nx, ny))
                            dist[Get(i, j, 0)][Get(nx, ny, 0)] = Tuple.Create(1, 0);

                    }

                    //Get Bishops distances
                    for (int k = -N + 1; k <= N - 1; k++)
                    {
                        int nx = i + k;
                        int ny = j + k;

                        if (In(nx, ny))
                        {
                            dist[Get(i, j, 1)][Get(nx, ny, 1)] = Tuple.Create(1, 0);
                        }

                        ny = j - k;
                        if (In(nx, ny))
                            dist[Get(i, j, 1)][Get(nx, ny, 1)] = Tuple.Create(1, 0);
                    }

                    //Get Rook distance
                    for (int k = 0; k < N; k++)
                    {
                        int nx = i;
                        int ny = k;
                        dist[Get(i, j, 2)][Get(nx, ny, 2)] = Tuple.Create(1, 0);

                        nx = k;
                        ny = j;

                        dist[Get(i, j, 2)][Get(nx, ny, 2)] = Tuple.Create(1, 0);
                    }

                    //Replace at the exact location
                    for (int k = 0; k < 3; k++)
                    {
                        for (int m = 0; m < 3; m++)
                        {
                            if (k != m)
                                dist[Get(i, j, k)][Get(i, j, m)] = Tuple.Create(1, 1);
                        }
                    }
                }
            }

            for (int i = 0; i < M; i++)
            {
                for (int j = 0; j < M; j++)
                {
                    for (int k = 0; k < M; k++)
                    {
                        if (dist[i][j].Item1 > dist[i][k].Item1 + dist[k][j].Item1)
                        {
                            dist[i][j] = Tuple.Create(dist[i][k].Item1 + dist[k][j].Item1,
                                dist[i][k].Item2 + dist[k][j].Item2);
                        }
                        else if (dist[i][j].Item1 == dist[i][k].Item1 + dist[k][j].Item1
                            && dist[i][j].Item2 > dist[i][k].Item2 + dist[k][j].Item2)
                        {
                            dist[i][j] = Tuple.Create(dist[i][j].Item1, dist[i][k].Item2 + dist[k][j].Item2);
                        }
                    }
                }
            }

            var dp = new Tuple<int, int>[N * N][];
            for (int i = 0; i < N * N; i++)
            {
                dp[i] = new Tuple<int, int>[3];

                for (int k = 0; k < 3; k++)
                {
                    dp[i][k] = Tuple.Create(100000, 100000);
                }
            }
            dp[0][0] = Tuple.Create(0, 0);
            dp[0][1] = Tuple.Create(0, 0);
            dp[0][2] = Tuple.Create(0, 0);

            for (int i = 0; i < N * N - 1; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    for (int k = 0; k < 3; k++)
                    {
                        var new_min = dp[i][j].Item1 + dist[Get(pos[i].Item1, pos[i].Item2, j)][Get(pos[i + 1].Item1, pos[i + 1].Item2, k)].Item1;
                        if (dp[i + 1][k].Item1 > new_min)
                        {
                            dp[i + 1][k] = Tuple.Create(new_min, dp[i][j].Item2 + dist[Get(pos[i].Item1, pos[i].Item2, j)][Get(pos[i + 1].Item1, pos[i + 1].Item2, k)].Item2);
                        }
                        else if (dp[i + 1][k].Item1 == new_min
                            && dp[i + 1][k].Item2 > dp[i][j].Item2 + dist[Get(pos[i].Item1, pos[i].Item2, j)][Get(pos[i + 1].Item1, pos[i + 1].Item2, k)].Item2)
                        {
                            dp[i + 1][k] = Tuple.Create(new_min, dp[i][j].Item2 + dist[Get(pos[i].Item1, pos[i].Item2, j)][Get(pos[i + 1].Item1, pos[i + 1].Item2, k)].Item2);
                        }
                    }
                }
            }

            var res = dp[N * N - 1][0];
            if (res.Item1 > dp[N * N - 1][1].Item1
                || (res.Item1 == dp[N * N - 1][1].Item1 && res.Item2 > dp[N * N - 1][1].Item2))
            {
                res = dp[N * N - 1][1];
            }
            if (res.Item1 > dp[N * N - 1][2].Item1
                || (res.Item1 == dp[N * N - 1][2].Item1 && res.Item2 > dp[N * N - 1][2].Item2))
            {
                res = dp[N * N - 1][2];
            }

            Console.WriteLine("" + res.Item1 + " " + res.Item2);
        }
    }
}


using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleTidbits
{
      class MinHeap<T> where T : IComparable
    {
        public IList<T> m_array;

        public MinHeap()
        {
            m_array = new List<T>();
        }
        public T GetRoot()
        {
            return m_array[0];
        }

        public T ExtractMin()
        {
            var result = m_array[0];
            m_array[0] = m_array[Count - 1];
            m_array.RemoveAt(m_array.Count - 1);
            ShiftDown(0);

            return result;
        }

        public void Remove(int index)
        {
        }

        public void ShiftDown(int index)
        {
            int left = 2 * index + 1;
            int right = 2 * index + 2;
            int Index = index;

            if (left < Count && m_array[index].CompareTo(m_array[left]) > 0)
            {
                index = left;
            }

            if (right < Count && m_array[index].CompareTo(m_array[right]) > 0)
            {
                index = right;
            }

            if (index != Index)
            {
                var temp = m_array[Index];
                m_array[Index] = m_array[index];
                m_array[index] = temp;

                ShiftDown(index);
            }
        }

        public int Count
        {
            get
            {
                return m_array.Count();
            }
        }

        private void Balance(int index)
        {
            if (index < 0)
            {
                return;
            }

            int parent = (index - 1) / 2;

            if (m_array[parent].CompareTo(m_array[index]) > 0)
            {
                var temp = m_array[parent];
                m_array[parent] = m_array[index];
                m_array[index] = temp;
                Balance(parent);
            }
        }

        public void Add(T number)
        {
            m_array.Add(number);
            //BuildHeap();
            Balance(m_array.Count - 1);
        }

        private void BuildHeap()
        {
            for (int i = Count / 2; i >= 0; i--)
            {
                Heapify(m_array, i);
            }
        }
        private void Heapify(IList<T> array, int index)
        {
            int N = array.Count;

            int Index = index;
            int left = 2 * index + 1;
            int right = 2 * index + 2;

            if (left < N && array[left].CompareTo(array[index]) < 0)
            {
                index = left;
            }

            if (right < N && array[right].CompareTo(array[index]) < 0)
            {
                index = right;
            }

            if (index != Index)
            {
                var temp = array[Index];
                array[Index] = array[index];
                array[index] = temp;

                Heapify(array, index);
            }

        }
    }

    class JustCodeForces
    {
        int N;
        int[,] board;
        Dictionary<int, Tuple<int, int>> locations;

        public static void Main()
        {
            JustCodeForces obj = new JustCodeForces();

            //while (true)
                obj.ThreePieces();
        }

        enum Pawn
        {
            Bishop = 0,
            Knight,
            Rook
        }
        class Move : IComparable
        {
            public int Moves { get; set; }
            public int Rep { get; set; }

            public int Num { get; set; }
            public Pawn pawn { get; set; }
            public int CompareTo(object o)
            {
                var other = o as Move;
                if (Moves < other.Moves) return -1;
                if (Moves > other.Moves) return 1;

                if (Moves == other.Moves) return Rep.CompareTo(other.Rep);

                return -1;
            }
        }
        private void ThreePieces()
        {
            N = int.Parse(Console.ReadLine());

            board = new int[N, N];
            locations = new Dictionary<int, Tuple<int, int>>();

            for (int i = 0; i < N; i++)
            {
                var array = Array.ConvertAll(Console.ReadLine().Split(new char[] { ' ' }), int.Parse);

                for (int j = 0; j < N; j++)
                {
                    board[i, j] = array[j];
                    locations.Add(array[j], Tuple.Create(i + 1, j + 1));
                }
            }

            //Min of all
            #region rook Distances
            //Find rook distances
            var rook_distances = new Tuple<int, int>[(N * N) + 1];
            rook_distances[1] = Tuple.Create(0, 0);

            for (int i = 2; i <= N * N; i++)
            {
                var current_location = locations[i - 1];
                var new_location = locations[i];

                if (current_location.Item1 == new_location.Item1 ||
                    current_location.Item2 == new_location.Item2)
                    rook_distances[i] = Tuple.Create(1, 0);
                else
                    rook_distances[i] = Tuple.Create(2, 0);
            }
            #endregion

            #region Bishop Distances
            //Find Bishop Distances
            var bishop_distances = new Tuple<int, int>[(N * N) + 1];
            bishop_distances[1] = Tuple.Create(0, 0);

            for (int i = 2; i <= N * N; i++)
            {
                var current_location = locations[i - 1];
                var new_location = locations[i];

                if ((current_location.Item1 + current_location.Item2) % 2 != (new_location.Item1 + new_location.Item2) % 2)
                    bishop_distances[i] = Tuple.Create(int.MaxValue, 0);
                else
                {
                    bool found = false;
                    //Move North East
                    for (int k = 1; k <= Math.Min(current_location.Item1 - 1, N - current_location.Item2); k++)
                    {
                        if (new_location.Item1 == current_location.Item1 - k && new_location.Item2 == current_location.Item2 + k)
                        {
                            bishop_distances[i] = Tuple.Create(1, 0);
                            found = true;
                            break;
                        }
                    }

                    //Move South west
                    if (!found)
                    {
                        for (int k = 1; k <= Math.Min(N - current_location.Item1, current_location.Item2 - 1); k++)
                        {
                            if (new_location.Item1 == current_location.Item1 + k && new_location.Item2 == current_location.Item2 - k)
                            {
                                bishop_distances[i] = Tuple.Create(1, 0);
                                found = true;
                                break;
                            }
                        }
                    }

                    //Move South east
                    if (!found)
                    {
                        for (int k = 1; k <= Math.Min(N - current_location.Item1, N - current_location.Item2); k++)
                        {
                            if (new_location.Item1 == current_location.Item1 + k && new_location.Item2 == current_location.Item2 + k)
                            {
                                bishop_distances[i] = Tuple.Create(1, 0);
                                found = true;
                                break;
                            }
                        }
                    }

                    //Move North West
                    if (!found)
                    {
                        for (int k = 1; k <= Math.Min(current_location.Item1 - 1, current_location.Item2 - 1); k++)
                        {
                            if (new_location.Item1 == current_location.Item1 - k && new_location.Item2 == current_location.Item2 - k)
                            {
                                bishop_distances[i] = Tuple.Create(1, 0);
                                found = true;
                                break;
                            }
                        }
                    }

                    if (!found)
                    {
                        bishop_distances[i] = Tuple.Create(2, 0);
                    }
                }
            }
            #endregion

            #region Knight Distances
            //Find Knight Distances
            var X = new int[] { 2, 2, -2, -2, 1, 1, -1, -1 };
            var Y = new int[] { -1, 1, -1, 1, 2, -2, 2, -2 };

            var Knight_distances = new Tuple<int, int>[(N * N) + 1];
            Knight_distances[1] = Tuple.Create(0, 0);

            for (int i = 2; i <= N * N; i++)
            {
                var current_location = locations[i - 1];
                var new_location = locations[i];

                var visited = new bool[N + 1, N + 1];
                var queue = new Queue<Tuple<int, int>>();
                visited[current_location.Item1, current_location.Item2] = true;
                queue.Enqueue(Tuple.Create(current_location.Item1, current_location.Item2));
                int count = queue.Count;
                int level = 0;
                bool found = false;

                while (queue.Count > 0)
                {
                    level++;
                    count = queue.Count;

                    while (count > 0)
                    {
                        count--;
                        var item = queue.Dequeue();

                        for (int t = 0; t < 8; t++)
                        {
                            var new_X = item.Item1 + X[t];
                            var new_Y = item.Item2 + Y[t];

                            if (new_X == new_location.Item1 && new_Y == new_location.Item2)
                            {
                                Knight_distances[i] = Tuple.Create(level, 0);
                                found = true;
                                break;
                            }

                            if (new_X >= 1 && new_X <= N && new_Y >= 1 && new_Y <= N && !visited[new_X, new_Y])
                            {
                                queue.Enqueue(Tuple.Create(new_X, new_Y));
                                visited[new_X, new_Y] = true;
                            }
                        }
                    }

                    if (found) break;
                }

                if (!found) Knight_distances[i] = Tuple.Create(int.MaxValue, 0);

            }
            #endregion

            var minheap = new MinHeap<Move>();

            var states = new Move[3][];
            states[0] = new Move[(N * N) + 1];
            states[1] = new Move[(N * N) + 1];
            states[2] = new Move[(N * N) + 1];

            for (int j = 1; j <= N * N; j++)
                states[0][j] = new Move() { pawn = Pawn.Bishop, Num = j, Moves = int.MaxValue, Rep = int.MaxValue };
            for (int j = 1; j <= N * N; j++)
                states[1][j] = new Move() { pawn = Pawn.Knight, Num = j, Moves = int.MaxValue, Rep = int.MaxValue };
            for (int j = 1; j <= N * N; j++)
                states[2][j] = new Move() { pawn = Pawn.Rook, Num = j, Moves = int.MaxValue, Rep = int.MaxValue };

            states[0][1] = new Move() { pawn = Pawn.Bishop, Num = 1, Moves = 0, Rep = 0 };
            states[1][1] = new Move() { pawn = Pawn.Knight, Num = 1, Moves = 0, Rep = 0 };
            states[2][1] = new Move() { pawn = Pawn.Rook, Num = 1, Moves = 0, Rep = 0 };

            minheap.Add(states[0][1]);
            minheap.Add(states[1][1]);
            minheap.Add(states[2][1]);

            while (minheap.Count > 0)
            {
                var item = minheap.ExtractMin();

                Pawn p = item.pawn;
                int currentnum = item.Num;
                int intPaws = (int)p;
                //Console.WriteLine(" -> " + item.Moves);
                //  Console.WriteLine("*******************");
                //  Console.WriteLine("" + p + " currentnum " + currentnum);

                if (currentnum == N * N) break;

                if (p == Pawn.Bishop)
                {
                    //Get Bishop distance
                    var dist = bishop_distances[currentnum + 1];
                    if (dist.Item1 != int.MaxValue)
                    {
                        if (item.Moves + dist.Item1 < states[0][currentnum + 1].Moves
                            || (item.Moves + dist.Item1 == states[0][currentnum + 1].Moves && item.Rep < states[0][currentnum + 1].Rep))
                        {
                            states[0][currentnum + 1] = new Move()
                            {
                                Num = currentnum + 1,
                                Moves = item.Moves + dist.Item1,
                                pawn = Pawn.Bishop,
                                Rep = item.Rep
                            };

                            minheap.Add(states[0][currentnum + 1]);
                        }
                    }

                    //Get Knight distance
                    dist = Knight_distances[currentnum + 1];
                    if (dist.Item1 != int.MaxValue)
                    {
                        if (item.Moves + dist.Item1 + 1 < states[1][currentnum + 1].Moves
                            || (item.Moves + dist.Item1 + 1 == states[1][currentnum + 1].Moves && item.Rep + 1 < states[1][currentnum + 1].Rep))
                        {
                            states[1][currentnum + 1] = new Move()
                            {
                                Num = currentnum + 1,
                                Moves = item.Moves + dist.Item1 + 1,
                                pawn = Pawn.Knight,
                                Rep = item.Rep + 1
                            };

                            minheap.Add(states[1][currentnum + 1]);
                        }
                    }

                    //Get Rook distance
                    dist = rook_distances[currentnum + 1];
                    if (dist.Item1 != int.MaxValue)
                    {
                        if (item.Moves + dist.Item1 + 1 < states[2][currentnum + 1].Moves
                            || (item.Moves + dist.Item1 + 1 == states[2][currentnum + 1].Moves && item.Rep + 1 < states[2][currentnum + 1].Rep))
                        {
                            states[2][currentnum + 1] = new Move()
                            {
                                Num = currentnum + 1,
                                Moves = item.Moves + dist.Item1 + 1,
                                pawn = Pawn.Rook,
                                Rep = item.Rep + 1
                            };

                            minheap.Add(states[2][currentnum + 1]);
                        }
                    }
                }
                else if (p == Pawn.Knight)
                {
                    var dist = Knight_distances[currentnum + 1];
                    if (dist.Item1 != int.MaxValue)
                    {
                        if (item.Moves + dist.Item1 < states[1][currentnum + 1].Moves
                            || (item.Moves + dist.Item1 == states[1][currentnum + 1].Moves && item.Rep < states[1][currentnum + 1].Rep))
                        {
                            states[1][currentnum + 1] = new Move()
                            {
                                Num = currentnum + 1,
                                Moves = item.Moves + dist.Item1,
                                pawn = Pawn.Knight,
                                Rep = item.Rep
                            };

                            minheap.Add(states[1][currentnum + 1]);
                        }
                    }

                    dist = rook_distances[currentnum + 1];
                    if (dist.Item1 != int.MaxValue)
                    {
                        if (item.Moves + dist.Item1 + 1 < states[2][currentnum + 1].Moves
                            || (item.Moves + dist.Item1 + 1 == states[2][currentnum + 1].Moves && item.Rep + 1 < states[2][currentnum + 1].Rep))
                        {
                            states[2][currentnum + 1] = new Move()
                            {
                                Num = currentnum + 1,
                                Moves = item.Moves + dist.Item1 + 1,
                                pawn = Pawn.Rook,
                                Rep = item.Rep + 1
                            };

                            minheap.Add(states[2][currentnum + 1]);
                        }
                    }

                    dist = bishop_distances[currentnum + 1];
                    if (dist.Item1 != int.MaxValue)
                    {
                        if (item.Moves + dist.Item1 + 1 < states[0][currentnum + 1].Moves
                            || (item.Moves + dist.Item1 + 1 == states[0][currentnum + 1].Moves && item.Rep + 1 < states[0][currentnum + 1].Rep))
                        {
                            states[0][currentnum + 1] = new Move()
                            {
                                Num = currentnum + 1,
                                Moves = item.Moves + dist.Item1 + 1,
                                pawn = Pawn.Bishop,
                                Rep = item.Rep + 1
                            };

                            minheap.Add(states[0][currentnum + 1]);
                        }
                    }
                }
                else if (p == Pawn.Rook)
                {
                    var dist = rook_distances[currentnum + 1];
                    if (dist.Item1 != int.MaxValue)
                    {
                        if (item.Moves + dist.Item1 < states[2][currentnum + 1].Moves
                            || (item.Moves + dist.Item1 == states[2][currentnum + 1].Moves && item.Rep < states[2][currentnum + 1].Rep))
                        {
                            states[2][currentnum + 1] = new Move()
                            {
                                Num = currentnum + 1,
                                Moves = item.Moves + dist.Item1,
                                pawn = Pawn.Rook,
                                Rep = item.Rep
                            };

                            minheap.Add(states[2][currentnum + 1]);
                        }
                    }

                    dist = bishop_distances[currentnum + 1];
                    if (dist.Item1 != int.MaxValue)
                    {
                        if (item.Moves + dist.Item1 + 1 < states[0][currentnum + 1].Moves
                            || (item.Moves + dist.Item1 + 1 == states[0][currentnum + 1].Moves && item.Rep + 1 < states[0][currentnum + 1].Rep))
                        {
                            states[0][currentnum + 1] = new Move()
                            {
                                Num = currentnum + 1,
                                Moves = item.Moves + dist.Item1 + 1,
                                pawn = Pawn.Bishop,
                                Rep = item.Rep + 1
                            };

                            minheap.Add(states[0][currentnum + 1]);
                        }
                    }

                    dist = Knight_distances[currentnum + 1];
                    if (dist.Item1 != int.MaxValue)
                    {
                        if (item.Moves + dist.Item1 + 1 < states[1][currentnum + 1].Moves
                            || (item.Moves + dist.Item1 + 1 == states[1][currentnum + 1].Moves && item.Rep + 1 < states[1][currentnum + 1].Rep))
                        {
                            states[1][currentnum + 1] = new Move()
                            {
                                Num = currentnum + 1,
                                Moves = item.Moves + dist.Item1 + 1,
                                pawn = Pawn.Knight,
                                Rep = item.Rep + 1
                            };

                            minheap.Add(states[1][currentnum + 1]);
                        }
                    }
                }
            }

            int res = int.MaxValue;

            res = Math.Min(states[1][N * N].Moves, res);
            res = Math.Min(states[2][N * N].Moves, res);
            res = Math.Min(states[0][N * N].Moves, res);

            int moves = int.MaxValue;

            if (res == states[0][N * N].Moves)
                moves = Math.Min(states[0][N * N].Rep, moves);

            if (res == states[1][N * N].Moves)
                moves = Math.Min(states[1][N * N].Rep, moves);
            if (res == states[2][N * N].Moves)
                moves = Math.Min(states[2][N * N].Rep, moves);

            Console.WriteLine("" + res + " " + moves);
        }

    }
}


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

 class MinHeap<T> where T : IComparable<T>
    {
        public IList<T> m_array;

        public MinHeap()
        {
            m_array = new List<T>();
        }
        public T GetRoot()
        {
            return m_array[0];
        }

        public T ExtractMin()
        {
            var result = m_array[0];
            m_array[0] = m_array[Count - 1];
            m_array.RemoveAt(m_array.Count - 1);
            ShiftDown(0);

            return result;
        }

        public void Remove(int index)
        {
        }
        public void ShiftDown(int index)
        {
            int left = 2 * index + 1;
            int right = 2 * index + 2;
            int Index = index;

            if (left < Count && m_array[index].CompareTo(m_array[left]) > 0)
            {
                index = left;
            }

            if (right < Count && m_array[index].CompareTo(m_array[right]) > 0)
            {
                index = right;
            }

            if (index != Index)
            {
                var temp = m_array[Index];
                m_array[Index] = m_array[index];
                m_array[index] = temp;

                ShiftDown(index);
            }
        }

        public int Count
        {
            get
            {
                return m_array.Count();
            }
        }

        private void Balance(int index)
        {
            if (index < 0)
            {
                return;
            }

            int parent = (index - 1) / 2;

            if (m_array[parent].CompareTo(m_array[index]) > 0)
            {
                var temp = m_array[parent];
                m_array[parent] = m_array[index];
                m_array[index] = temp;
                Balance(parent);
            }
        }

        public void Add(T number)
        {
            m_array.Add(number);
            BuildHeap();
            //Balance(m_array.Count - 1);
        }

        private void BuildHeap()
        {
            for (int i = Count / 2; i >= 0; i--)
            {
                Heapify(m_array, i);
            }
        }

        private void Heapify(IList<T> array, int index)
        {
            int N = array.Count;

            int Index = index;
            int left = 2 * index + 1;
            int right = 2 * index + 2;

            if (left < N && array[left].CompareTo(array[index]) < 0)
            {
                index = left;
            }

            if (right < N && array[right].CompareTo(array[index]) < 0)
            {
                index = right;
            }

            if (index != Index)
            {
                var temp = array[Index];
                array[Index] = array[index];
                array[index] = temp;

                Heapify(array, index);
            }

        }
    }

class testcodeforces
{
    int N;
    int[,] board;
    Dictionary<int, Tuple<int, int>> locations;

    public static void Main()
    {
        testcodeforces obj = new testcodeforces();
        obj.ThreePieces();
    }

    enum Pawn
    {
        Bishop = 0,
        Knight,
        Rook
    }
    class Move : IComparable<Move>
    {
        public int Moves { get; set; }
        public int Rep { get; set; }

        public int Num { get; set; }
        public Pawn pawn { get; set; }
        public int CompareTo(Move other)
        {
            if (Moves < other.Moves) return -1;
            if (Moves > other.Moves) return 1;

            if (Moves == other.Moves) return Rep.CompareTo(other.Rep);

            return 0;
        }
    }
    private void ThreePieces()
    {
        N = int.Parse(Console.ReadLine());

        board = new int[N, N];
        locations = new Dictionary<int, Tuple<int, int>>();

        for (int i = 0; i < N; i++)
        {
            var array = Array.ConvertAll(Console.ReadLine().Split(new char[] { ' ' }), int.Parse);

            for (int j = 0; j < N; j++)
            {
                board[i, j] = array[j];
                locations.Add(array[j], Tuple.Create(i + 1, j + 1));
            }
        }

        //Min of all
        #region rook Distances
        //Find rook distances
        var rook_distances = new Tuple<int, int>[(N * N) + 1];
        rook_distances[1] = Tuple.Create(0, 0);

        for (int i = 2; i <= N * N; i++)
        {
            var current_location = locations[i - 1];
            var new_location = locations[i];

            if (current_location.Item1 == new_location.Item1 ||
                current_location.Item2 == new_location.Item2)
                rook_distances[i] = Tuple.Create(1, 0);
            else
                rook_distances[i] = Tuple.Create(2, 0);
        }
        #endregion

        #region Bishop Distances
        //Find Bishop Distances
        var bishop_distances = new Tuple<int, int>[(N * N) + 1];
        bishop_distances[1] = Tuple.Create(0, 0);

        for (int i = 2; i <= N * N; i++)
        {
            var current_location = locations[i - 1];
            var new_location = locations[i];

            if ((current_location.Item1 + current_location.Item2) % 2 != (new_location.Item1 + new_location.Item2) % 2)
                bishop_distances[i] = Tuple.Create(int.MaxValue, 0);
            else
            {
                bool found = false;
                //Move North East
                for (int k = 1; k <= Math.Min(current_location.Item1 - 1, N - current_location.Item2); k++)
                {
                    if (new_location.Item1 == current_location.Item1 - k && new_location.Item2 == current_location.Item2 + k)
                    {
                        bishop_distances[i] = Tuple.Create(1, 0);
                        found = true;
                        break;
                    }
                }

                //Move South west
                if (!found)
                {
                    for (int k = 1; k <= Math.Min(N - current_location.Item1, current_location.Item2 - 1); k++)
                    {
                        if (new_location.Item1 == current_location.Item1 + k && new_location.Item2 == current_location.Item2 - k)
                        {
                            bishop_distances[i] = Tuple.Create(1, 0);
                            found = true;
                            break;
                        }
                    }
                }

                //Move South east
                if (!found)
                {
                    for (int k = 1; k <= Math.Min(N - current_location.Item1, N - current_location.Item2 - 1); k++)
                    {
                        if (new_location.Item1 == current_location.Item1 + k && new_location.Item2 == current_location.Item2 + k)
                        {
                            bishop_distances[i] = Tuple.Create(1, 0);
                            found = true;
                            break;
                        }
                    }
                }

                //Move North West
                if (!found)
                {
                    for (int k = 1; k <= Math.Min(current_location.Item1 - 1, current_location.Item2 - 1); k++)
                    {
                        if (new_location.Item1 == current_location.Item1 - k && new_location.Item2 == current_location.Item2 - k)
                        {
                            bishop_distances[i] = Tuple.Create(1, 0);
                            found = true;
                            break;
                        }
                    }
                }

                if (!found)
                {
                    bishop_distances[i] = Tuple.Create(2, 0);
                }
            }
        }
        #endregion

        #region Knight Distances
        //Find Knight Distances
        var X = new int[] { 2, 2, -2, -2, 1, 1, -1, -1 };
        var Y = new int[] { -1, 1, -1, 1, 2, -2, 2, -2 };

        var Knight_distances = new Tuple<int, int>[(N * N) + 1];
        Knight_distances[1] = Tuple.Create(0, 0);

        for (int i = 2; i <= N * N; i++)
        {
            var current_location = locations[i - 1];
            var new_location = locations[i];

            var list = new List<Tuple<int, int>>();

            bool found = false;

            for (int t = 0; t < 8; t++)
            {
                var new_X = current_location.Item1 + X[t];
                var new_Y = current_location.Item2 + Y[t];
                if (new_X == new_location.Item1 && new_Y == new_location.Item2)
                {
                    Knight_distances[i] = Tuple.Create(1, 0);
                    found = true;
                    break;
                }
                else
                {
                    if (new_X >= 1 && new_X <= N && new_Y >= 1 && new_Y <= N)
                        list.Add(Tuple.Create(new_X, new_Y));
                }
            }

            if (!found)
            {
                for (int l = 0; l < list.Count; l++)
                {
                    for (int t = 0; t < 8; t++)
                    {
                        var new_X = list[l].Item1 + X[t];
                        var new_Y = list[l].Item2 + Y[t];
                        if (new_X == new_location.Item1 && new_Y == new_location.Item2)
                        {
                            Knight_distances[i] = Tuple.Create(2, 0);
                            found = true;
                            break;
                        }
                    }
                }
            }

            if (!found) Knight_distances[i] = Tuple.Create(int.MaxValue, 0);

        }
        #endregion

        var minheap = new MinHeap<Move>();

        var states = new Move[3][];
        states[0] = new Move[(N * N) + 1];
        states[1] = new Move[(N * N) + 1];
        states[2] = new Move[(N * N) + 1];

        for (int j = 1; j <= N * N; j++)
            states[0][j] = new Move() { pawn = Pawn.Bishop, Num = j, Moves = int.MaxValue, Rep = int.MaxValue };
        for (int j = 1; j <= N * N; j++)
            states[1][j] = new Move() { pawn = Pawn.Knight, Num = j, Moves = int.MaxValue, Rep = int.MaxValue };
        for (int j = 1; j <= N * N; j++)
            states[2][j] = new Move() { pawn = Pawn.Rook, Num = j, Moves = int.MaxValue, Rep = int.MaxValue };

        states[0][1] = new Move() { pawn = Pawn.Bishop, Num = 1, Moves = 0, Rep = 0 };
        states[1][1] = new Move() { pawn = Pawn.Knight, Num = 1, Moves = 0, Rep = 0 };
        states[2][1] = new Move() { pawn = Pawn.Rook, Num = 1, Moves = 0, Rep = 0 };

        minheap.Add(states[0][1]);
        minheap.Add(states[1][1]);
        minheap.Add(states[2][1]);

        while (minheap.Count > 0)
        {
            var item = minheap.ExtractMin();

            Pawn p = item.pawn;
            int currentnum = item.Num;
            int intPaws = (int)p;

            Console.WriteLine("*******************");
            Console.WriteLine("" + p + " currentnum " + currentnum);

            if (currentnum == N * N) break;

            if (p == Pawn.Bishop)
            {
                //Get Bishop distance
                var dist = bishop_distances[currentnum + 1];
                if (dist.Item1 != int.MaxValue)
                {
                    if (item.Moves + dist.Item1 < states[0][currentnum + 1].Moves
                        || (item.Moves + dist.Item1 == states[0][currentnum + 1].Moves && item.Rep < states[0][currentnum + 1].Rep))
                    {
                        states[0][currentnum + 1] = new Move()
                        {
                            Num = currentnum + 1,
                            Moves = item.Moves + dist.Item1,
                            pawn = Pawn.Bishop,
                            Rep = item.Rep
                        };

                        minheap.Add(states[0][currentnum + 1]);
                    }
                }

                //Get Knight distance
                dist = Knight_distances[currentnum + 1];
                if (dist.Item1 != int.MaxValue)
                {
                    if (item.Moves + dist.Item1 + 1 < states[1][currentnum + 1].Moves
                        || (item.Moves + dist.Item1 + 1 == states[1][currentnum + 1].Moves && item.Rep + 1 < states[1][currentnum + 1].Rep))
                    {
                        states[1][currentnum + 1] = new Move()
                        {
                            Num = currentnum + 1,
                            Moves = item.Moves + dist.Item1 + 1,
                            pawn = Pawn.Knight,
                            Rep = item.Rep + 1
                        };

                        minheap.Add(states[1][currentnum + 1]);
                    }
                }

                //Get Rook distance
                dist = rook_distances[currentnum + 1];
                if (dist.Item1 != int.MaxValue)
                {
                    if (item.Moves + dist.Item1 + 1 < states[2][currentnum + 1].Moves
                        || (item.Moves + dist.Item1 + 1 == states[2][currentnum + 1].Moves && item.Rep + 1 < states[2][currentnum + 1].Rep))
                    {
                        states[2][currentnum + 1] = new Move()
                        {
                            Num = currentnum + 1,
                            Moves = item.Moves + dist.Item1 + 1,
                            pawn = Pawn.Rook,
                            Rep = item.Rep + 1
                        };

                        minheap.Add(states[2][currentnum + 1]);
                    }
                }
            }
            else if (p == Pawn.Knight)
            {
                var dist = Knight_distances[currentnum + 1];
                if (dist.Item1 != int.MaxValue)
                {
                    if (item.Moves + dist.Item1 < states[1][currentnum + 1].Moves
                        || (item.Moves + dist.Item1 == states[1][currentnum + 1].Moves && item.Rep < states[1][currentnum + 1].Rep))
                    {
                        states[1][currentnum + 1] = new Move()
                        {
                            Num = currentnum + 1,
                            Moves = item.Moves + dist.Item1,
                            pawn = Pawn.Knight,
                            Rep = item.Rep
                        };

                        minheap.Add(states[1][currentnum + 1]);
                    }
                }

                dist = rook_distances[currentnum + 1];
                if (dist.Item1 != int.MaxValue)
                {
                    if (item.Moves + dist.Item1 + 1 < states[2][currentnum + 1].Moves
                        || (item.Moves + dist.Item1 + 1 == states[2][currentnum + 1].Moves && item.Rep + 1 < states[2][currentnum + 1].Rep))
                    {
                        states[2][currentnum + 1] = new Move()
                        {
                            Num = currentnum + 1,
                            Moves = item.Moves + dist.Item1 + 1,
                            pawn = Pawn.Rook,
                            Rep = item.Rep + 1
                        };

                        minheap.Add(states[2][currentnum + 1]);
                    }
                }

                dist = bishop_distances[currentnum + 1];
                if (dist.Item1 != int.MaxValue)
                {
                    if (item.Moves + dist.Item1 + 1 < states[0][currentnum + 1].Moves
                        || (item.Moves + dist.Item1 + 1 == states[0][currentnum + 1].Moves && item.Rep + 1 < states[0][currentnum + 1].Rep))
                    {
                        states[0][currentnum + 1] = new Move()
                        {
                            Num = currentnum + 1,
                            Moves = item.Moves + dist.Item1 + 1,
                            pawn = Pawn.Bishop,
                            Rep = item.Rep + 1
                        };

                        minheap.Add(states[0][currentnum + 1]);
                    }
                }
            }
            else if (p == Pawn.Rook)
            {
                var dist = rook_distances[currentnum + 1];
                if (dist.Item1 != int.MaxValue)
                {
                    if (item.Moves + dist.Item1 < states[2][currentnum + 1].Moves
                        || (item.Moves + dist.Item1 == states[2][currentnum + 1].Moves && item.Rep < states[2][currentnum + 1].Rep))
                    {
                        states[2][currentnum + 1] = new Move()
                        {
                            Num = currentnum + 1,
                            Moves = item.Moves + dist.Item1,
                            pawn = Pawn.Rook,
                            Rep = item.Rep
                        };

                        minheap.Add(states[2][currentnum + 1]);
                    }
                }

                dist = bishop_distances[currentnum + 1];
                if (dist.Item1 != int.MaxValue)
                {
                    if (item.Moves + dist.Item1 + 1 < states[0][currentnum + 1].Moves
                        || (item.Moves + dist.Item1 + 1 == states[0][currentnum + 1].Moves && item.Rep + 1 < states[0][currentnum + 1].Rep))
                    {
                        states[0][currentnum + 1] = new Move()
                        {
                            Num = currentnum + 1,
                            Moves = item.Moves + dist.Item1 + 1,
                            pawn = Pawn.Bishop,
                            Rep = item.Rep + 1
                        };

                        minheap.Add(states[0][currentnum + 1]);
                    }
                }

                dist = Knight_distances[currentnum + 1];
                if (dist.Item1 != int.MaxValue)
                {
                    if (item.Moves + dist.Item1 + 1 < states[2][currentnum + 1].Moves
                        || (item.Moves + dist.Item1 + 1 == states[2][currentnum + 1].Moves && item.Rep + 1 < states[2][currentnum + 1].Rep))
                    {
                        states[2][currentnum + 1] = new Move()
                        {
                            Num = currentnum + 1,
                            Moves = item.Moves + dist.Item1 + 1,
                            pawn = Pawn.Knight,
                            Rep = item.Rep + 1
                        };

                        minheap.Add(states[2][currentnum + 1]);
                    }
                }
            }
        }

        int res = int.MaxValue;

        res = Math.Min(states[1][N * N].Moves, res);
        res = Math.Min(states[2][N * N].Moves, res);
        res = Math.Min(states[0][N * N].Moves, res);

        int moves = int.MaxValue;

        if (res == states[0][N * N].Moves)
            moves = Math.Min(states[0][N * N].Rep, moves);

        if (res == states[1][N * N].Moves)
            moves = Math.Min(states[1][N * N].Rep, moves);
        if (res == states[2][N * N].Moves)
            moves = Math.Min(states[2][N * N].Rep, moves);

        Console.WriteLine("" + res + " " + moves);
    }
}


using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleTidbits
{
    class JustCodeForces
    {
        int N;
        int[,] board;
        Dictionary<int, Tuple<int, int>> locations;

        public static void Main()
        {
            JustCodeForces obj = new JustCodeForces();

            //while (true)
                obj.ThreePieces_ContestAnalysis();
        }

        int M;
        Tuple<int, int>[][] dist;
        Tuple<int, int>[] pos;

        int[] X = new int[] { 2, 2, -2, -2, 1, 1, -1, -1 };
        int[] Y = new int[] { -1, 1, -1, 1, 2, -2, 2, -2 };
        private bool In(int x, int y)
        {
            if (x >= 0 && y >= 0 && x < N && y < N) return true;
            return false;
        }

        private int Get(int x, int y, int p)
        {
            return x * 3 * N + y * 3 + p;
        }
        private void ThreePieces_ContestAnalysis()
        {
            N = int.Parse(Console.ReadLine());

            pos = new Tuple<int, int>[N * N];
            M = (N - 1) * N * 3 + N * 3 + 3;
            dist = new Tuple<int, int>[M][];
            for (int i = 0; i < M; i++)
                dist[i] = new Tuple<int, int>[M];

            for (int i = 0; i < N; i++)
            {
                var array = Array.ConvertAll(Console.ReadLine().Split(new char[] { ' ' }), int.Parse);

                for (int j = 0; j < N; j++)
                {
                    pos[--array[j]] = Tuple.Create(i, j);
                }
            }

            for (int i = 0; i < M; i++)
            {
                for (int j = 0; j < M; j++)
                {
                    dist[i][j] = Tuple.Create(100000, 100000);
                    if (i == j) dist[i][j] = Tuple.Create(0, 0);
                }
            }

            for (int i = 0; i < N; i++)
            {
                for (int j = 0; j < N; j++)
                {
                    //Get Knights distance
                    for (int k = 0; k < 8; k++)
                    {
                        int nx = i + X[k];
                        int ny = j + Y[k];

                        if (In(nx, ny))
                            dist[Get(i, j, 0)][Get(nx, ny, 0)] = Tuple.Create(1, 0);

                    }

                    //Get Bishops distances
                    for (int k = -N + 1; k <= N - 1; k++)
                    {
                        int nx = i + k;
                        int ny = j + k;

                        if (In(nx, ny))
                        {
                            dist[Get(i, j, 1)][Get(nx, ny, 1)] = Tuple.Create(1, 0);
                        }

                        ny = j - k;
                        if (In(nx, ny))
                            dist[Get(i, j, 1)][Get(nx, ny, 1)] = Tuple.Create(1, 0);
                    }

                    //Get Rook distance
                    for (int k = 0; k < N; k++)
                    {
                        int nx = i;
                        int ny = k;
                        dist[Get(i, j, 2)][Get(nx, ny, 2)] = Tuple.Create(1, 0);

                        nx = k;
                        ny = j;

                        dist[Get(i, j, 2)][Get(nx, ny, 2)] = Tuple.Create(1, 0);
                    }

                    //Replace at the exact location
                    for (int k = 0; k < 3; k++)
                    {
                        for (int m = 0; m < 3; m++)
                        {
                            if (k != m)
                                dist[Get(i, j, k)][Get(i, j, m)] = Tuple.Create(1, 1);
                        }
                    }
                }
            }

            for (int i = 0; i < M; i++)
            {
                for (int j = 0; j < M; j++)
                {
                    for (int k = 0; k < M; k++)
                    {
                        if (dist[i][j].Item1 > dist[i][k].Item1 + dist[k][j].Item1)
                        {
                            dist[i][j] = Tuple.Create(dist[i][k].Item1 + dist[k][j].Item1,
                                dist[i][k].Item2 + dist[k][j].Item2);
                        }
                        else if (dist[i][j].Item1 == dist[i][k].Item1 + dist[k][j].Item1
                            && dist[i][j].Item2 > dist[i][k].Item2 + dist[k][j].Item2)
                        {
                            dist[i][j] = Tuple.Create(dist[i][j].Item1, dist[i][k].Item2 + dist[k][j].Item2);
                        }
                    }
                }
            }

            var dp = new Tuple<int, int>[N * N][];
            for (int i = 0; i < N * N; i++)
            {
                dp[i] = new Tuple<int, int>[3];

                for (int k = 0; k < 3; k++)
                {
                    dp[i][k] = Tuple.Create(int.MaxValue, int.MaxValue);
                }
            }
            dp[0][0] = Tuple.Create(0, 0);
            dp[0][1] = Tuple.Create(0, 0);
            dp[0][2] = Tuple.Create(0, 0);

            for (int i = 0; i < N * N - 1; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    for (int k = 0; k < 3; k++)
                    {
                        var new_min = dp[i][j].Item1 + dist[Get(pos[i].Item1, pos[i].Item2, j)][Get(pos[i + 1].Item1, pos[i + 1].Item2, k)].Item1;
                        if (dp[i + 1][k].Item1 > new_min)
                        {
                            dp[i + 1][k] = Tuple.Create(new_min, dp[i][j].Item2 + dist[Get(pos[i].Item1, pos[i].Item2, j)][Get(pos[i + 1].Item1, pos[i + 1].Item2, k)].Item2);
                        }
                        else if (dp[i + 1][k].Item1 == new_min
                            && dp[i + 1][k].Item2 > dp[i][j].Item2 + dist[Get(pos[i].Item1, pos[i].Item2, j)][Get(pos[i + 1].Item1, pos[i + 1].Item2, k)].Item2)
                        {
                            dp[i + 1][k] = Tuple.Create(new_min, dp[i][j].Item2 + dist[Get(pos[i].Item1, pos[i].Item2, j)][Get(pos[i + 1].Item1, pos[i + 1].Item2, k)].Item2);
                        }
                    }
                }
            }

            var res = dp[N * N - 1][0];
            if (res.Item1 > dp[N * N - 1][1].Item1
                || (res.Item1 == dp[N * N - 1][1].Item1 && res.Item2 > dp[N * N - 1][1].Item2))
            {
                res = dp[N * N - 1][1];
            }
            if (res.Item1 > dp[N * N - 1][2].Item1
                || (res.Item1 == dp[N * N - 1][2].Item1 && res.Item2 > dp[N * N - 1][2].Item2))
            {
                res = dp[N * N - 1][2];
            }

            Console.WriteLine("" + res.Item1 + " " + res.Item2);
        }
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Text;
using System.Diagnostics;
using System.Numerics;
using static System.Console;
using static System.Convert;
using static System.Math;
using static Template;

class Solver
{
    public Scanner sc;
    public void Solve()
    {
        N = sc.Int;
        var A = new int[N][];
        var R = new int[N * N+1];R[N * N] = -1;
        for (int i = 0; i < N; i++)
        {
            A[i] = sc.ArrInt;
            for (int j = 0; j < N; j++)
            {
                A[i][j]--;
                R[A[i][j]] = Id(i, j);
            }
        }
        //dist:=iにいて，jまでクリアしていて，駒が0:knight,1:bishop,2:rookのとき
        var dist = Create(N * N, () => Create(N * N, () => Create(3, () => long.MaxValue / 2)));
        var pq = new PriorityQueue<(long c, int now, int cl, int pi)>((a, b) => a.c.CompareTo(b.c));
        dist[R[0]][0][0] = dist[R[0]][0][1] = dist[R[0]][0][2] = 0;
        pq.Push((0, R[0], 0, 0));
        pq.Push((0, R[0], 0, 1));
        pq.Push((0, R[0], 0, 2));
        while (pq.Any())
        {
            var (cst, now, cl, pi) = pq.Pop();
            int h = now / N, w = now % N;
            if (pi == 0)
            {
                for(int k = 0; k < knH.Length; k++)
                {
                    int nh = h + knH[k], nw = w + knW[k];
                    if (!Inside(nh, nw, N, N)) continue;
                    var p = cl;
                    if (Id(nh, nw) == R[cl + 1]) p++;
                    if (chmin(ref dist[Id(nh, nw)][p][pi], cst + W))
                    {
                        pq.Push((cst + W, Id(nh, nw), p, pi));
                    }
                }
            }
            else if (pi == 1)
            {
                for(int i = -N + 1; i <= N - 1; i++)
                {
                    {
                        int nh = i + h, nw = i + w;
                        if (!Inside(nh, nw, N, N)) goto SEC;
                        var p = cl;
                        if (Id(nh, nw) == R[cl + 1]) p++;
                        if (chmin(ref dist[Id(nh, nw)][p][pi], cst + W))
                        {
                            pq.Push((cst + W, Id(nh, nw), p, pi));
                        }
                    }
                SEC:;
                    {
                        int nh = i + h, nw = -i + w;
                        if (!Inside(nh, nw, N, N)) continue;
                        var p = cl;
                        if (Id(nh, nw) == R[cl + 1]) p++;
                        if (chmin(ref dist[Id(nh, nw)][p][pi], cst + W))
                        {
                            pq.Push((cst + W, Id(nh, nw), p, pi));
                        }
                    }
                }
            }
            else
            {
                for(int i = -N + 1; i <= N - 1; i++)
                {

                    {
                        int nh = h, nw = i + w;
                        if (!Inside(nh, nw, N, N)) goto SEC;
                        var p = cl;
                        if (Id(nh, nw) == R[cl + 1]) p++;
                        if (chmin(ref dist[Id(nh, nw)][p][pi], cst + W))
                        {
                            pq.Push((cst + W, Id(nh, nw), p, pi));
                        }
                    }
                SEC:;
                    {
                        int nh = i + h, nw = w;
                        if (!Inside(nh, nw, N, N)) continue;
                        var p = cl;
                        if (Id(nh, nw) == R[cl + 1]) p++;
                        if (chmin(ref dist[Id(nh, nw)][p][pi], cst + W))
                        {
                            pq.Push((cst + W, Id(nh, nw), p, pi));
                        }
                    }
                }
            }
            for(int k = 0; k < 3; k++)
            {
                if(chmin(ref dist[now][cl][k],cst+1))
                {
                    pq.Push((cst + 1, now, cl, k));
                }
            }
        }
        var res = dist[R[N * N - 1]][N * N - 1].Min();
        Console.WriteLine($"{res / W} {res % W}");
    }
    int N;
    const long W = (long)1e9;
    public int Id(int h, int w) => h * N + w;
    int[] knH = new[] { -2, -2, -1, -1, 1, 1, 2, 2 }, knW = new[] { 1, -1, 2, -2, 2, -2, 1, -1 };
    public static bool Inside(int h, int w, int H, int W)
        => 0 <= h && h < H && 0 <= w && w < W;
}

public class PriorityQueue<T>
{
    private List<T> data = new List<T>();
    private Comparison<T> cmp;
    public int Count { get { return data.Count; } }
    public T Top { get { return data[0]; } }
    public PriorityQueue() { cmp = cmp ?? Comparer<T>.Default.Compare; }

    public PriorityQueue(Comparison<T> comparison) { cmp = comparison; }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private int Parent(int i)
        => (i - 1) >> 1;
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private int Left(int i)
        => (i << 1) + 1;
    public T Push(T val)
    {
        int i = data.Count;
        data.Add(val);
        while (i > 0)
        {
            int p = Parent(i);
            if (cmp(data[p], val) <= 0)
                break;
            data[i] = data[p];
            i = p;
        }
        data[i] = val;
        return val;
    }
    public T Pop()
    {
        var ret = data[0];
        var p = 0;
        var x = data[data.Count - 1];
        while (Left(p) < data.Count - 1)
        {
            var l = Left(p);
            if (l < data.Count - 2 && cmp(data[l + 1], data[l]) < 0) l++;
            if (cmp(data[l], x) >= 0)
                break;
            data[p] = data[l];
            p = l;
        }
        data[p] = x;
        data.RemoveAt(data.Count - 1);
        return ret;
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool Any() => data.Count > 0;
}
#region Template
public static class Template
{
    static void Main(string[] args)
    {
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        var sol = new Solver() { sc = new Scanner() };
        //var th = new Thread(sol.Solve, 1 << 26);th.Start();th.Join();
        sol.Solve();
        Console.Out.Flush();
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmin<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) > 0) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmax<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) < 0) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(ref T a, ref T b) { var t = b; b = a; a = t; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(this IList<T> A, int i, int j) { var t = A[i]; A[i] = A[j]; A[j] = t; }
    public static T[] Create<T>(int n, Func<T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(); return rt; }
    public static T[] Create<T>(int n, Func<int, T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(i); return rt; }
    public static void Out<T>(this IList<T> A, out T a) => a = A[0];
    public static void Out<T>(this IList<T> A, out T a, out T b) { a = A[0]; b = A[1]; }
    public static void Out<T>(this IList<T> A, out T a, out T b, out T c) { A.Out(out a, out b); c = A[2]; }
    public static void Out<T>(this IList<T> A, out T a, out T b, out T c, out T d) { A.Out(out a, out b, out c); d = A[3]; }
    public static string Concat<T>(this IEnumerable<T> A, string sp) => string.Join(sp, A);
    public static char ToChar(this int s, char begin = '0') => (char)(s + begin);
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> A) => A.OrderBy(v => Guid.NewGuid());
    public static int CompareTo<T>(this T[] A, T[] B, Comparison<T> cmp = null) { cmp = cmp ?? Comparer<T>.Default.Compare; for (var i = 0; i < Min(A.Length, B.Length); i++) { int c = cmp(A[i], B[i]); if (c > 0) return 1; else if (c < 0) return -1; } if (A.Length == B.Length) return 0; if (A.Length > B.Length) return 1; else return -1; }
    public static int ArgMax<T>(this IList<T> A, Comparison<T> cmp = null) { cmp = cmp ?? Comparer<T>.Default.Compare; T max = A[0]; int rt = 0; for (int i = 1; i < A.Count; i++) if (cmp(max, A[i]) < 0) { max = A[i]; rt = i; } return rt; }
    public static T PopBack<T>(this List<T> A) { var v = A[A.Count - 1]; A.RemoveAt(A.Count - 1); return v; }
    public static void Fail<T>(T s) { Console.WriteLine(s); Console.Out.Close(); Environment.Exit(0); }
}
public class Scanner
{
    public string Str => Console.ReadLine().Trim();
    public int Int => int.Parse(Str);
    public long Long => long.Parse(Str);
    public double Double => double.Parse(Str);
    public int[] ArrInt => Str.Split(' ').Select(int.Parse).ToArray();
    public long[] ArrLong => Str.Split(' ').Select(long.Parse).ToArray();
    public char[][] Grid(int n) => Create(n, () => Str.ToCharArray());
    public int[] ArrInt1D(int n) => Create(n, () => Int);
    public long[] ArrLong1D(int n) => Create(n, () => Long);
    public int[][] ArrInt2D(int n) => Create(n, () => ArrInt);
    public long[][] ArrLong2D(int n) => Create(n, () => ArrLong);
    private Queue<string> q = new Queue<string>();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Next<T>() { if (q.Count == 0) foreach (var item in Str.Split(' ')) q.Enqueue(item); return (T)Convert.ChangeType(q.Dequeue(), typeof(T)); }
    public void Make<T1>(out T1 v1) => v1 = Next<T1>();
    public void Make<T1, T2>(out T1 v1, out T2 v2) { v1 = Next<T1>(); v2 = Next<T2>(); }
    public void Make<T1, T2, T3>(out T1 v1, out T2 v2, out T3 v3) { Make(out v1, out v2); v3 = Next<T3>(); }
    public void Make<T1, T2, T3, T4>(out T1 v1, out T2 v2, out T3 v3, out T4 v4) { Make(out v1, out v2, out v3); v4 = Next<T4>(); }
    public void Make<T1, T2, T3, T4, T5>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5) { Make(out v1, out v2, out v3, out v4); v5 = Next<T5>(); }
    public void Make<T1, T2, T3, T4, T5, T6>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5, out T6 v6) { Make(out v1, out v2, out v3, out v4, out v5); v6 = Next<T6>(); }
    public void Make<T1, T2, T3, T4, T5, T6, T7>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5, out T6 v6, out T7 v7) { Make(out v1, out v2, out v3, out v4, out v5, out v6); v7 = Next<T7>(); }
    public (T1, T2) Make<T1, T2>() { Make(out T1 v1, out T2 v2); return (v1, v2); }
    public (T1, T2, T3) Make<T1, T2, T3>() { Make(out T1 v1, out T2 v2, out T3 v3); return (v1, v2, v3); }
    public (T1, T2, T3, T4) Make<T1, T2, T3, T4>() { Make(out T1 v1, out T2 v2, out T3 v3, out T4 v4); return (v1, v2, v3, v4); }
}

#endregion


using System;
using CompLib.Util;

public class Program
{
    private int N;
    private int[][] A;

    private int[] X, Y;

    private readonly int[] kDx = new int[] {-2, -2, -1, -1, 1, 1, 2, 2};
    private readonly int[] kDy = new int[] {-1, 1, -2, 2, -2, 2, -1, 1};

    private const int C = 200;

    public void Solve()
    {
        var sc = new Scanner();
        N = sc.NextInt();
        A = new int[N][];
        for (int i = 0; i < N; i++)
        {
            A[i] = sc.IntArray();
        }

        X = new int[N * N];
        Y = new int[N * N];
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                X[A[i][j] - 1] = i;
                Y[A[i][j] - 1] = j;
            }
        }

        var kDist = new long[N, N, N, N];
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                for (int k = 0; k < N; k++)
                {
                    for (int l = 0; l < N; l++)
                    {
                        kDist[i, j, k, l] = int.MaxValue;
                    }
                }
            }
        }

        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                for (int k = 0; k < kDx.Length; k++)
                {
                    int ii = i + kDx[k];
                    int jj = j + kDy[k];
                    if (ii < 0 || ii >= N) continue;
                    if (jj < 0 || jj >= N) continue;
                    kDist[i, j, ii, jj] = 1;
                }
            }
        }

        for (int ki = 0; ki < N; ki++)
        {
            for (int kj = 0; kj < N; kj++)
            {
                for (int ii = 0; ii < N; ii++)
                {
                    for (int ij = 0; ij < N; ij++)
                    {
                        for (int ji = 0; ji < N; ji++)
                        {
                            for (int jj = 0; jj < N; jj++)
                            {
                                kDist[ii, ij, ji, jj] = Math.Min(kDist[ii, ij, ji, jj],
                                    kDist[ii, ij, ki, kj] + kDist[ki, kj, ji, jj]);
                            }
                        }
                    }
                }
            }
        }

        /*
         * N*Nマス
         *
         * ナイト、ビショップ、ルークのどれかを1のマスに置く
         *
         * 2に向かう、3... N^2
         *
         * 駒を変えるか移動するか
         *
         * 移動回数最小のパス
         * そのうち入れ替え最小パス
         *
         */

        // iに行く、交換j回移動回数最小
        var dp = new long[N * N, 3, C];
        for (int i = 0; i < N * N; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                for (int k = 0; k < C; k++)
                {
                    dp[i, j, k] = int.MaxValue;
                }
            }
        }

        dp[0, 0, 0] = 0;
        dp[0, 1, 0] = 0;
        dp[0, 2, 0] = 0;
        for (int i = 0; i < N * N - 1; i++)
        {
            int nx = X[i + 1];
            int ny = Y[i + 1];

            int x = X[i];
            int y = Y[i];
            {
                // ナイト
                for (int j = 0; j < C; j++)
                {
                    if (dp[i, 0, j] == int.MaxValue) continue;
                    {
                        // ナイトのまま次に行く
                        long kd = kDist[x, y, nx, ny];
                        if (kd == int.MaxValue) continue;
                        dp[i + 1, 0, j] = Math.Min(dp[i + 1, 0, j], dp[i, 0, j] + kd);
                    }
                    if (j + 1 < C)
                    {
                        {
                            // ビショップに変える ななめ
                            if ((x + y) % 2 != (nx + ny) % 2) continue;
                            int dx = Math.Abs(nx - x);
                            int dy = Math.Abs(ny - y);
                            int bd = dx == dy ? 1 : 2;
                            dp[i + 1, 1, j + 1] = Math.Min(dp[i + 1, 1, j + 1], dp[i, 0, j] + bd + 1);
                        }

                        {
                            // ルークに変える
                            int rd = (x == nx) || (y == ny) ? 1 : 2;
                            dp[i + 1, 2, j + 1] = Math.Min(dp[i + 1, 2, j + 1], dp[i, 0, j] + rd + 1);
                        }
                    }
                }
            }

            {
                // ビショップ

                for (int j = 0; j < C; j++)
                {
                    if (dp[i, 1, j] == int.MaxValue) continue;
                    {
                        // ビショップ
                        if ((x + y) % 2 != (nx + ny) % 2) continue;
                        int dx = Math.Abs(nx - x);
                        int dy = Math.Abs(ny - y);
                        int bd = dx == dy ? 1 : 2;
                        dp[i + 1, 1, j] = Math.Min(dp[i + 1, 1, j], dp[i, 1, j] + bd);
                    }

                    if (j + 1 < C)
                    {
                        {
                            // ナイト
                            long kd = kDist[x, y, nx, ny];
                            if (kd == int.MaxValue) continue;
                            dp[i + 1, 0, j + 1] = Math.Min(dp[i + 1, 0, j + 1], dp[i, 1, j] + kd + 1);
                        }
                        {
                            // ルークに変える
                            int rd = (x == nx) || (y == ny) ? 1 : 2;
                            dp[i + 1, 2, j + 1] = Math.Min(dp[i + 1, 2, j + 1], dp[i, 1, j] + rd + 1);
                        }
                    }
                }
            }

            {
                // ルーク
                for (int j = 0; j < C; j++)
                {
                    if (dp[i, 2, j] == int.MaxValue) continue;
                    {
                        // ルークに変える
                        int rd = (x == nx) || (y == ny) ? 1 : 2;
                        dp[i + 1, 2, j] = Math.Min(dp[i + 1, 2, j], dp[i, 2, j] + rd);
                    }

                    if (j + 1 < C)
                    {
                        {
                            // ナイト
                            long kd = kDist[x, y, nx, ny];
                            if (kd == int.MaxValue) continue;
                            dp[i + 1, 0, j + 1] = Math.Min(dp[i + 1, 0, j + 1], dp[i, 2, j] + kd + 1);
                        }

                        {
                            // ビショップ
                            if ((x + y) % 2 != (nx + ny) % 2) continue;
                            int dx = Math.Abs(nx - x);
                            int dy = Math.Abs(ny - y);
                            int bd = dx == dy ? 1 : 2;
                            dp[i + 1, 1, j + 1] = Math.Min(dp[i + 1, 1, j + 1], dp[i, 2, j] + bd + 1);
                        }
                    }
                }
            }
        }

        long step = long.MaxValue;
        int c = 0;

        for (int i = 0; i < C; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                if (dp[N * N - 1, j, i] < step)
                {
                    step = dp[N * N - 1, j, i];
                    c = i;
                }
            }
        }

        Console.WriteLine($"{step} {c}");
    }

    public static void Main(string[] args) => new Program().Solve();
}

namespace CompLib.Util
{
    using System;
    using System.Linq;

    class Scanner
    {
        private string[] _line;
        private int _index;
        private const char Separator = ' ';

        public Scanner()
        {
            _line = new string[0];
            _index = 0;
        }

        public string Next()
        {
            if (_index >= _line.Length)
            {
                string s;
                do
                {
                    s = Console.ReadLine();
                } while (s.Length == 0);

                _line = s.Split(Separator);
                _index = 0;
            }

            return _line[_index++];
        }

        public string ReadLine()
        {
            _index = _line.Length;
            return Console.ReadLine();
        }

        public int NextInt() => int.Parse(Next());
        public long NextLong() => long.Parse(Next());
        public double NextDouble() => double.Parse(Next());
        public decimal NextDecimal() => decimal.Parse(Next());
        public char NextChar() => Next()[0];
        public char[] NextCharArray() => Next().ToCharArray();

        public string[] Array()
        {
            string s = Console.ReadLine();
            _line = s.Length == 0 ? new string[0] : s.Split(Separator);
            _index = _line.Length;
            return _line;
        }

        public int[] IntArray() => Array().Select(int.Parse).ToArray();
        public long[] LongArray() => Array().Select(long.Parse).ToArray();
        public double[] DoubleArray() => Array().Select(double.Parse).ToArray();
        public decimal[] DecimalArray() => Array().Select(decimal.Parse).ToArray();
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Text;
using System.Diagnostics;
using System.Numerics;
using static System.Console;
using static System.Convert;
using static System.Math;
using static Template;

class Solver
{
    public Scanner sc;
    public void Solve()
    {
        N = sc.Int;
        var A = new int[N][];
        var R = new int[N * N+1];R[N * N] = -1;
        for (int i = 0; i < N; i++)
        {
            A[i] = sc.ArrInt;
            for (int j = 0; j < N; j++)
            {
                A[i][j]--;
                R[A[i][j]] = Id(i, j);
            }
        }
        //dist:=iにいて，jまでクリアしていて，駒が0:knight,1:bishop,2:rookのとき
        var dist = Create(N * N, () => Create(N * N, () => Create(3, () => long.MaxValue / 2)));
        var pq = new PriorityQueue<(long c, int now, int cl, int pi)>((a, b) => a.c.CompareTo(b.c));
        dist[R[0]][0][0] = dist[R[0]][0][1] = dist[R[0]][0][2] = 0;
        pq.Push((0, R[0], 0, 0));
        pq.Push((0, R[0], 0, 1));
        pq.Push((0, R[0], 0, 2));
        while (pq.Any())
        {
            var (cst, now, cl, pi) = pq.Pop();
            int h = now / N, w = now % N;
            if (pi == 0)
            {
                for(int k = 0; k < knH.Length; k++)
                {
                    int nh = h + knH[k], nw = w + knW[k];
                    if (!Inside(nh, nw, N, N)) continue;
                    var p = cl;
                    if (Id(nh, nw) == R[cl + 1]) p++;
                    if (chmin(ref dist[Id(nh, nw)][p][pi], cst + W))
                    {
                        pq.Push((cst + W, Id(nh, nw), p, pi));
                    }
                }
            }
            else if (pi == 1)
            {
                for(int i = -N + 1; i <= N - 1; i++)
                {
                    {
                        int nh = i + h, nw = i + w;
                        if (!Inside(nh, nw, N, N)) goto SEC;
                        var p = cl;
                        if (Id(nh, nw) == R[cl + 1]) p++;
                        if (chmin(ref dist[Id(nh, nw)][p][pi], cst + W))
                        {
                            pq.Push((cst + W, Id(nh, nw), p, pi));
                        }
                    }
                SEC:;
                    {
                        int nh = i + h, nw = -i + w;
                        if (!Inside(nh, nw, N, N)) continue;
                        var p = cl;
                        if (Id(nh, nw) == R[cl + 1]) p++;
                        if (chmin(ref dist[Id(nh, nw)][p][pi], cst + W))
                        {
                            pq.Push((cst + W, Id(nh, nw), p, pi));
                        }
                    }
                }
            }
            else
            {
                for(int i = -N + 1; i <= N - 1; i++)
                {

                    {
                        int nh = h, nw = i + w;
                        if (!Inside(nh, nw, N, N)) goto SEC;
                        var p = cl;
                        if (Id(nh, nw) == R[cl + 1]) p++;
                        if (chmin(ref dist[Id(nh, nw)][p][pi], cst + W))
                        {
                            pq.Push((cst + W, Id(nh, nw), p, pi));
                        }
                    }
                SEC:;
                    {
                        int nh = i + h, nw = w;
                        if (!Inside(nh, nw, N, N)) continue;
                        var p = cl;
                        if (Id(nh, nw) == R[cl + 1]) p++;
                        if (chmin(ref dist[Id(nh, nw)][p][pi], cst + W))
                        {
                            pq.Push((cst + W, Id(nh, nw), p, pi));
                        }
                    }
                }
            }
            for(int k = 0; k < 3; k++)
            {
                if(chmin(ref dist[now][cl][k],cst+1+W))
                {
                    pq.Push((cst + 1+W, now, cl, k));
                }
            }
        }
        var res = dist[R[N * N - 1]][N * N - 1].Min();
        Console.WriteLine($"{res / W} {res % W}");
    }
    int N;
    const long W = (long)1e9;
    public int Id(int h, int w) => h * N + w;
    int[] knH = new[] { -2, -2, -1, -1, 1, 1, 2, 2 }, knW = new[] { 1, -1, 2, -2, 2, -2, 1, -1 };
    public static bool Inside(int h, int w, int H, int W)
        => 0 <= h && h < H && 0 <= w && w < W;
}

public class PriorityQueue<T>
{
    private List<T> data = new List<T>();
    private Comparison<T> cmp;
    public int Count { get { return data.Count; } }
    public T Top { get { return data[0]; } }
    public PriorityQueue() { cmp = cmp ?? Comparer<T>.Default.Compare; }

    public PriorityQueue(Comparison<T> comparison) { cmp = comparison; }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private int Parent(int i)
        => (i - 1) >> 1;
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private int Left(int i)
        => (i << 1) + 1;
    public T Push(T val)
    {
        int i = data.Count;
        data.Add(val);
        while (i > 0)
        {
            int p = Parent(i);
            if (cmp(data[p], val) <= 0)
                break;
            data[i] = data[p];
            i = p;
        }
        data[i] = val;
        return val;
    }
    public T Pop()
    {
        var ret = data[0];
        var p = 0;
        var x = data[data.Count - 1];
        while (Left(p) < data.Count - 1)
        {
            var l = Left(p);
            if (l < data.Count - 2 && cmp(data[l + 1], data[l]) < 0) l++;
            if (cmp(data[l], x) >= 0)
                break;
            data[p] = data[l];
            p = l;
        }
        data[p] = x;
        data.RemoveAt(data.Count - 1);
        return ret;
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool Any() => data.Count > 0;
}
#region Template
public static class Template
{
    static void Main(string[] args)
    {
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        var sol = new Solver() { sc = new Scanner() };
        //var th = new Thread(sol.Solve, 1 << 26);th.Start();th.Join();
        sol.Solve();
        Console.Out.Flush();
    }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmin<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) > 0) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmax<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) < 0) { a = b; return true; } return false; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(ref T a, ref T b) { var t = b; b = a; a = t; }
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(this IList<T> A, int i, int j) { var t = A[i]; A[i] = A[j]; A[j] = t; }
    public static T[] Create<T>(int n, Func<T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(); return rt; }
    public static T[] Create<T>(int n, Func<int, T> f) { var rt = new T[n]; for (var i = 0; i < rt.Length; ++i) rt[i] = f(i); return rt; }
    public static void Out<T>(this IList<T> A, out T a) => a = A[0];
    public static void Out<T>(this IList<T> A, out T a, out T b) { a = A[0]; b = A[1]; }
    public static void Out<T>(this IList<T> A, out T a, out T b, out T c) { A.Out(out a, out b); c = A[2]; }
    public static void Out<T>(this IList<T> A, out T a, out T b, out T c, out T d) { A.Out(out a, out b, out c); d = A[3]; }
    public static string Concat<T>(this IEnumerable<T> A, string sp) => string.Join(sp, A);
    public static char ToChar(this int s, char begin = '0') => (char)(s + begin);
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> A) => A.OrderBy(v => Guid.NewGuid());
    public static int CompareTo<T>(this T[] A, T[] B, Comparison<T> cmp = null) { cmp = cmp ?? Comparer<T>.Default.Compare; for (var i = 0; i < Min(A.Length, B.Length); i++) { int c = cmp(A[i], B[i]); if (c > 0) return 1; else if (c < 0) return -1; } if (A.Length == B.Length) return 0; if (A.Length > B.Length) return 1; else return -1; }
    public static int ArgMax<T>(this IList<T> A, Comparison<T> cmp = null) { cmp = cmp ?? Comparer<T>.Default.Compare; T max = A[0]; int rt = 0; for (int i = 1; i < A.Count; i++) if (cmp(max, A[i]) < 0) { max = A[i]; rt = i; } return rt; }
    public static T PopBack<T>(this List<T> A) { var v = A[A.Count - 1]; A.RemoveAt(A.Count - 1); return v; }
    public static void Fail<T>(T s) { Console.WriteLine(s); Console.Out.Close(); Environment.Exit(0); }
}
public class Scanner
{
    public string Str => Console.ReadLine().Trim();
    public int Int => int.Parse(Str);
    public long Long => long.Parse(Str);
    public double Double => double.Parse(Str);
    public int[] ArrInt => Str.Split(' ').Select(int.Parse).ToArray();
    public long[] ArrLong => Str.Split(' ').Select(long.Parse).ToArray();
    public char[][] Grid(int n) => Create(n, () => Str.ToCharArray());
    public int[] ArrInt1D(int n) => Create(n, () => Int);
    public long[] ArrLong1D(int n) => Create(n, () => Long);
    public int[][] ArrInt2D(int n) => Create(n, () => ArrInt);
    public long[][] ArrLong2D(int n) => Create(n, () => ArrLong);
    private Queue<string> q = new Queue<string>();
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public T Next<T>() { if (q.Count == 0) foreach (var item in Str.Split(' ')) q.Enqueue(item); return (T)Convert.ChangeType(q.Dequeue(), typeof(T)); }
    public void Make<T1>(out T1 v1) => v1 = Next<T1>();
    public void Make<T1, T2>(out T1 v1, out T2 v2) { v1 = Next<T1>(); v2 = Next<T2>(); }
    public void Make<T1, T2, T3>(out T1 v1, out T2 v2, out T3 v3) { Make(out v1, out v2); v3 = Next<T3>(); }
    public void Make<T1, T2, T3, T4>(out T1 v1, out T2 v2, out T3 v3, out T4 v4) { Make(out v1, out v2, out v3); v4 = Next<T4>(); }
    public void Make<T1, T2, T3, T4, T5>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5) { Make(out v1, out v2, out v3, out v4); v5 = Next<T5>(); }
    public void Make<T1, T2, T3, T4, T5, T6>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5, out T6 v6) { Make(out v1, out v2, out v3, out v4, out v5); v6 = Next<T6>(); }
    public void Make<T1, T2, T3, T4, T5, T6, T7>(out T1 v1, out T2 v2, out T3 v3, out T4 v4, out T5 v5, out T6 v6, out T7 v7) { Make(out v1, out v2, out v3, out v4, out v5, out v6); v7 = Next<T7>(); }
    public (T1, T2) Make<T1, T2>() { Make(out T1 v1, out T2 v2); return (v1, v2); }
    public (T1, T2, T3) Make<T1, T2, T3>() { Make(out T1 v1, out T2 v2, out T3 v3); return (v1, v2, v3); }
    public (T1, T2, T3, T4) Make<T1, T2, T3, T4>() { Make(out T1 v1, out T2 v2, out T3 v3, out T4 v4); return (v1, v2, v3, v4); }
}

#endregion


/*
csc -debug D.cs && mono --debug D.exe <<< "3
1 9 3
8 6 7
4 2 5"
K: (1,1) -> (3,2) -> (1,3) -> (3,2) -> L -> (3,1) -> (3,3) -> (3,2) -> (2,2) -> (2,3) -> (2,1) -> (1,1) -> (1,2)
    1        2        3                      4        5                 6        7        8                 9
*/

using System;
using System.Collections.Generic;
using System.Linq;

public class HelloWorld {
    static public void Main () {
        SolveCodeForces();
    }

    private enum Fig { K, L, S };

    private static int N;
    private static int[][] arr;

    public static void SolveCodeForces() {
        N = int.Parse(Console.ReadLine());
        arr = new int[N][];
        for (var i = 0; i < arr.Length; i++)
            arr[i] = Console.ReadLine().Split().Select(int.Parse).ToArray();

        var numPos = new int[N*N+1][];
        for (var r = 0; r < arr.Length; r++)
            for (var c = 0; c < arr[r].Length; c++)
                numPos[ arr[r][c] ] = new[] {r, c};

        var bestAnswers = new Dictionary<Fig, int[]>{
            { Fig.K, new[] {0, 0} },
            { Fig.L, new[] {0, 0} },
            { Fig.S, new[] {0, 0} },
        };
        for (var d = 2; d <= N * N; d++) {
            var rcFrom = numPos[d - 1];
            var hopAns = new Dictionary<Fig, int[]>{
                { Fig.K, new[] {int.MaxValue, int.MaxValue} },
                { Fig.L, new[] {int.MaxValue, int.MaxValue} },
                { Fig.S, new[] {int.MaxValue, int.MaxValue} },
            };
            foreach (var fig in AllFigs) {
                if (bestAnswers[fig][0] < int.MaxValue)
                    foreach (var state in SolveHop(fig, rcFrom[0], rcFrom[1], d)) {
                        // System.Console.WriteLine("Digits: {0}->{1}, start fig {5}, end fig {2}, moves {3}, switches {4}", d-1, d, state.Fig, state.Moves, state.Switches, fig);
                        var newMoves = bestAnswers[fig][0] + state.Moves;
                        var newSwitches = bestAnswers[fig][1] + state.Switches;

                        var prevAns = hopAns[state.Fig];
                        if (prevAns[0] == int.MaxValue) {
                            prevAns[0] = newMoves;
                            prevAns[1] = newSwitches;
                        }
                        else if (newMoves < prevAns[0] || (newMoves == prevAns[0] && newSwitches < prevAns[1])) {
                            prevAns[0] = newMoves;
                            prevAns[1] = newSwitches;
                        }
                    }
            }
            bestAnswers = hopAns;
        }

        var bestAns = bestAnswers.OrderBy(kvp => kvp.Value[0]).ThenBy(kvp => kvp.Value[1]).First().Value;
        System.Console.WriteLine("{0} {1}", bestAns[0], bestAns[1]);
    }

    private struct State { public int R; public int C; public Fig Fig; public int Moves; public int Switches; }

    private static bool AreIndicesAllowed(int r, int c) {
        return r >= 0 && r < N && c >= 0 && c < N;
    }

    private static readonly int[][] K_Moves = new[] {
        new[] { -2, -1 },
        new[] { -2, 1 },
        new[] { -1, -2 },
        new[] { -1, 2 },
        new[] { 1, -2 },
        new[] { 1, 2 },
        new[] { 2, -1 },
        new[] { 2, 1 },
    };

    private static readonly int[][] S_Moves = new[] {
        new[] { -1, -1 },
        new[] { -1, 1 },
        new[] { 1, -1 },
        new[] { 1, 1 },
    };

    private static Fig[] AllFigs = new[] { Fig.L, Fig.S, Fig.K };

    private static IEnumerable<State> ExpandState(State state) {
        if (state.Fig == Fig.K) {
            foreach (var drdc in K_Moves) {
                var r = drdc[0] + state.R;
                var c = drdc[1] + state.C;
                if (AreIndicesAllowed(r, c))
                    yield return new State { Fig = Fig.K, R = r, C = c, Moves = state.Moves + 1, Switches = state.Switches };
            }
        }
        else if (state.Fig == Fig.L) {
            for (var r = 0; r < N; r++)
                if (r != state.R)
                    yield return new State { Fig = Fig.L, R = r, C = state.C, Moves = state.Moves + 1, Switches = state.Switches };
            for (var c = 0; c < N; c++)
                if (c != state.C)
                    yield return new State { Fig = Fig.L, R = state.R, C = c, Moves = state.Moves + 1, Switches = state.Switches };
        }
        else if (state.Fig == Fig.S) {
            for (var d = 1; d < N; d++)
                foreach (var drdc in S_Moves) {
                    var r = d * drdc[0] + state.R;
                    var c = d * drdc[1] + state.C;
                    if (AreIndicesAllowed(r, c))
                        yield return new State { Fig = Fig.S, R = r, C = c, Moves = state.Moves + 1, Switches = state.Switches };
                }
        }
        foreach (var fig in AllFigs)
            if (fig != state.Fig)
                yield return new State { Fig = fig, R = state.R, C = state.C, Moves = state.Moves + 1, Switches = state.Switches + 1 };
    }

    // Returns: end fig => [ number of moves, number of fig switches ]
    private static IEnumerable<State> SolveHop(Fig startFig, int rFrom, int cFrom, int targetDigit) {
        var Q = new Queue<State>();
        Q.Enqueue(new State { Fig = startFig, R = rFrom, C = cFrom });
        while (Q.Count > 0) {
            var state = Q.Dequeue();
            if (arr[state.R][state.C] == targetDigit)
                yield return state;
            // There is an always solution with 3 moves: switch to L, hop horizontally, hop vertically
            else if (state.Moves < 3)
                foreach (var neigh in ExpandState(state)) {
                    Q.Enqueue(neigh);
                }
        }
    }
}

public static class Helpers {
    public static T[] CreateArray<T>(int length, Func<int, T> itemCreate) {
        var result = new T[length];
        for (var i = 0; i < result.Length; i++)
            result[i] = itemCreate(i);
        return result;
    }
}