#pragma warning disable using System; using System.IO; using System.Linq; using System.Collections.Generic; using System.Runtime.CompilerServices; using static System.Console; using static System.Math; using static Util; using System.Runtime.InteropServices.Marshalling; #region using(AtCoder等非対応) // using pii = (int, int); // using pll = (long, long); // using pdd = (double, double); // using pss = (string, string); // using pis = (int, string); // using psi = (string, int); // using pls = (long, string); // using psl = (string, long); // using pds = (double, string); // using psd = (string, double); // using pid = (int, double); // using pdi = (double, int); // using pld = (long, double); // using pdl = (double, long); // using vb = bool[]; // using vvb = bool[][]; // using vvvb = bool[][][]; // using vi = int[]; // using vvi = int[][]; // using vvvi = int[][][]; // using vl = long[]; // using vvl = long[][]; // using vvvl = long[][][]; // using vd = double[]; // using vvd = double[][]; // using vvvd = double[][][]; // using vs = string[]; // using vvs = string[][]; // using vvvs = string[][][]; // using listb = System.Collections.Generic.List; // using llistb = System.Collections.Generic.List>; // using lllistb = System.Collections.Generic.List>>; // using listi = System.Collections.Generic.List; // using llisti = System.Collections.Generic.List>; // using lllisti = System.Collections.Generic.List>>; // using listl = System.Collections.Generic.List; // using llistl = System.Collections.Generic.List>; // using lllistl = System.Collections.Generic.List>>; // using listd = System.Collections.Generic.List; // using llistd = System.Collections.Generic.List>; // using lllistd = System.Collections.Generic.List>>; // using lists = System.Collections.Generic.List; // using llists = System.Collections.Generic.List>; // using lllists = System.Collections.Generic.List>>; // using mii = System.Collections.Generic.SortedDictionary; // using mll = System.Collections.Generic.SortedDictionary; // using mss = System.Collections.Generic.SortedDictionary; // using mis = System.Collections.Generic.SortedDictionary; // using msi = System.Collections.Generic.SortedDictionary; // using mls = System.Collections.Generic.SortedDictionary; // using msl = System.Collections.Generic.SortedDictionary; // using umii = System.Collections.Generic.Dictionary; // using umll = System.Collections.Generic.Dictionary; // using umss = System.Collections.Generic.Dictionary; // using umis = System.Collections.Generic.Dictionary; // using umsi = System.Collections.Generic.Dictionary; // using umls = System.Collections.Generic.Dictionary; // using umsl = System.Collections.Generic.Dictionary; // using seti = System.Collections.Generic.SortedSet; // using setl = System.Collections.Generic.SortedSet; // using sets = System.Collections.Generic.SortedSet; // using useti = System.Collections.Generic.HashSet; // using usetl = System.Collections.Generic.HashSet; // using usets = System.Collections.Generic.HashSet; #endregion class Util { public static double PI = 3.141592653589793; public static long m107 = 1000000007; public static long m998 = 998244353; public static int a10_9 = 1000000000; public static long a10_18 = 1000000000000000000; public static int iinf = 1 << 31; public static long linf = (1l << 61) - (1l << 31); /// 1行読みこみ [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string read() => ReadLine(); /// 1行読みこみ [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string readln() => ReadLine(); /// 1行読みこみ [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string readline() => ReadLine(); /// 改行なし出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void write() => Write(""); /// 改行なし出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void write(T value) => Write(value); /// 改行あり出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void writeln() => WriteLine(""); /// 改行あり出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void writeln(T value) => WriteLine(value); /// 改行あり出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void writeline() => WriteLine(""); /// 改行あり出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void writeline(T value) => WriteLine(value); /// 改行なし出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void print() => Write(""); /// 改行なし出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void print(T value) => Write(value); /// 改行あり出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void println() => WriteLine(""); /// 改行あり出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void println(T value) => WriteLine(value); /// 改行あり出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void printline() => WriteLine(""); /// 改行あり出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void printline(T value) => WriteLine(value); /// 任意の要素数・初期値の配列を作って初期化する [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T[] makearr(int num, T value) { var arr = new T[num]; for (int i = 0; i < num; ++i) arr[i] = value; return arr; } // end of func /// 任意の要素数・初期値の2次元配列を作って初期化する [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T[][] makearr2(int height, int width, T value) { var arr = new T[height][]; for (int i = 0; i < height; ++i) { arr[i] = new T[width]; for (int j = 0; j < width; ++j) { arr[i][j] = value; } } return arr; } // end of func /// 任意の要素数・初期値の3次元配列を作って初期化する [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T[][][] makearr3(int height, int width, int depth, T value) { var arr = new T[height][][]; for (int i = 0; i < height; ++i) { arr[i] = new T[width][]; for (int j = 0; j < width; ++j) { arr[i][j] = new T[depth]; for (int k = 0; k < depth; ++k) { arr[i][j][k] = value; } } } return arr; } // end of func /// 任意の要素数・初期値のListを作って初期化する [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List makelist(int num, T value) { return new List(makearr(num, value)); } // end of func /// 任意の要素数・初期値の2次元Listを作って初期化する [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List> makelist2(int height, int width, T value) { var arr = new List>(); for (int i = 0; i < height; ++i) { arr.Add(makelist(width, value)); } return arr; } // end of func /// 任意の要素数・初期値の3次元Listを作って初期化する [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List>> makelist3(int height, int width, int depth, T value) { var arr = new List>>(); for (int i = 0; i < height; ++i) { arr[i] = new List>(); for (int j = 0; j < width; ++j) { arr[i].Add(makelist(depth, value)); } } return arr; } // end of func /// 1次元配列のディープコピーを行う [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T[] copyarr(T[] arr) { T[] brr = new T[arr.Length]; Array.Copy(arr, brr, arr.Length); return brr; } // end of func /// 2次元配列のディープコピーを行う [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T[][] copyarr2(T[][] arr) { T[][] brr = new T[arr.Length][]; for (int i = 0; i < arr.Length; ++i) { brr[i] = new T[arr[i].Length]; Array.Copy(arr[i], brr[i], arr[i].Length); } return brr; } // end of func /// 3次元配列のディープコピーを行う [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T[][][] copyarr3(T[][][] arr) { T[][][] brr = new T[arr.Length][][]; for (int i = 0; i < arr.Length; ++i) { brr[i] = new T[arr[i].Length][]; for (int j = 0; j < arr[i].Length; ++j) { brr[i][j] = new T[arr[i][j].Length]; Array.Copy(arr[i][j], brr[i][j], arr[i][j].Length); } } return brr; } // end of func /// 1次元Listのディープコピーを行う [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List copylist(List list) { return new List(list); } // end of func /// 2次元Listのディープコピーを行う [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List> copylist2(List> list) { List> list2 = new List>(); for (int i = 0; i < list.Count; ++i) { list2.Add(new List(list[i])); } return list2; } // end of func /// 3次元Listのディープコピーを行う [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List>> copylist3(List>> list) { List>> list2 = new List>>(); for (int i = 0; i < list.Count; ++i) { List> tmplist = new List>(); for (int j = 0; j < list[i].Count; ++i) { tmplist.Add(new List(list[i][j])); } list2.Add(tmplist); } return list2; } // end of func /// 1次元Listを出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void printlist(List list) { WriteLine(string.Join(" ", list)); } // end of func /// 1次元配列を出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void printlist(T[] list) { WriteLine(string.Join(" ", list)); } // end of func /// 2次元リストを出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void printlist2(List> list) { foreach (var l in list) { WriteLine(string.Join(" ", l)); } } // end of func /// 2次元配列を出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void printlist2(T[][] list) { foreach (var l in list) { WriteLine(string.Join(" ", l)); } } // end of func /// 1次元Listを出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void printarr(List list) { WriteLine(string.Join(" ", list)); } // end of func /// 1次元配列を出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void printarr(T[] list) { WriteLine(string.Join(" ", list)); } // end of func /// 2次元リストを出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void printarr2(List> list) { foreach (var l in list) { WriteLine(string.Join(" ", l)); } } // end of func /// 2次元配列を出力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void printarr2(T[][] list) { foreach (var l in list) { WriteLine(string.Join(" ", l)); } } // end of func /// 数字を1つint型で読み込み [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int readint() { return int.Parse(ReadLine()); } // end of func /// 数字を1つlong型で読み込み [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long readlong() { return long.Parse(ReadLine()); } // end of func /// 入力を空白区切りのstringで返す(変則的な入力に対応) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string[] readsplit() { return ReadLine().Split(' '); } // end of func /// 数字をスペース区切りでint型で入力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int[] readints() { return ReadLine().Split(' ').Select(_ => int.Parse(_)).ToArray(); } // end of func /// 数字をスペース区切りでlong型で入力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long[] readlongs() { return ReadLine().Split(' ').Select(_ => long.Parse(_)).ToArray(); } // end of func /// 数字をスペース区切りでfloat型で入力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float[] readfloats() { return ReadLine().Split(' ').Select(_ => float.Parse(_)).ToArray(); } // end of func /// 数字をスペース区切りでdouble型で入力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double[] readdoubles() { return ReadLine().Split(' ').Select(_ => double.Parse(_)).ToArray(); } // end of func /// 文字列をスペース区切りで入力 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string[] readstrings() { return ReadLine().Split(' '); } // end of func /// 読み込んだint2つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (int, int) readintt2() { var arr = readints(); return (arr[0], arr[1]); } // end of func /// 読み込んだint3つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (int, int, int) readintt3() { var arr = readints(); return (arr[0], arr[1], arr[2]); } // end of func /// 読み込んだint4つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (int, int, int, int) readintt4() { var arr = readints(); return (arr[0], arr[1], arr[2], arr[3]); } // end of func /// 読み込んだlong2つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (long, long) readlongt2() { var arr = readlongs(); return (arr[0], arr[1]); } // end of func /// 読み込んだ数long3つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (long, long, long) readlongt3() { var arr = readlongs(); return (arr[0], arr[1], arr[2]); } // end of func /// 読み込んだ数long4つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (long, long, long, long) readlongt4() { var arr = readlongs(); return (arr[0], arr[1], arr[2], arr[3]); } // end of func /// 読み込んだfloat2つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (float, float) readfloatt2() { var arr = readfloats(); return (arr[0], arr[1]); } // end of func /// 読み込んだfloat3つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (float, float, float) readfloatt3() { var arr = readfloats(); return (arr[0], arr[1], arr[2]); } // end of func /// 読み込んだfloat4つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (float, float, float, float) readfloatt4() { var arr = readfloats(); return (arr[0], arr[1], arr[2], arr[3]); } // end of func /// 読み込んだdouble2つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (double, double) readdoublet2() { var arr = readdoubles(); return (arr[0], arr[1]); } // end of func /// 読み込んだdouble3つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (double, double, double) readdoublet3() { var arr = readdoubles(); return (arr[0], arr[1], arr[2]); } // end of func /// 読み込んだdouble4つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (double, double, double, double) readdoublet4() { var arr = readdoubles(); return (arr[0], arr[1], arr[2], arr[3]); } // end of func /// 読み込んだstring2つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (string, string) readstringt2() { var arr = ReadLine().Split(' '); return (arr[0], arr[1]); } // end of func /// 読み込んだstring3つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (string, string, string) readstringt3() { var arr = ReadLine().Split(' '); return (arr[0], arr[1], arr[2]); } // end of func /// 読み込んだstring3つをタプルで返す(分解代入用) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (string, string, string, string) readstringt4() { var arr = ReadLine().Split(' '); return (arr[0], arr[1], arr[2], arr[3]); } // end of func /// 先頭に要素数(int)と次にでかい数字1つ [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (int, long) readintlongt2() { var arr = ReadLine().Split(' ').Select(x => long.Parse(x)).ToArray(); return ((int)arr[0], arr[1]); } // end of func /// 先頭に要素数(int)と次にでかい数字2つ [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (int, long, long) readintlongt3() { var arr = ReadLine().Split(' ').Select(x => long.Parse(x)).ToArray(); return ((int)arr[0], arr[1], arr[2]); } // end of func /// 先頭に要素数(int)と次にでかい数字2つ [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (int, long, long, long) readintlongt4() { var arr = ReadLine().Split(' ').Select(x => long.Parse(x)).ToArray(); return ((int)arr[0], arr[1], arr[2], arr[3]); } // end of func /// 小数点以下を16桁で表示(精度が厳しい問題に対応) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteLine16(T num) { WriteLine(string.Format("{0:0.################}", num)); } // end of func /// 整数を二進数で表示 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteLine2bit(int num) { WriteLine(Convert.ToString(num, 2)); } // end of func /// 整数を二進数で表示 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteLine2bit(long num) { WriteLine(Convert.ToString(num, 2)); } // end of func /// 整数を2進数表現した文字列に [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string IntToString2bit(int num) { return Convert.ToString(num, 2); } // end of func /// 整数を2進数表現した文字列に [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string LongToString2bit(long num) { return Convert.ToString(num, 2); } // end of func /// 出力のflush削除 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void preprocess() { var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false }; System.Console.SetOut(sw); } // end of func /// 出力をflush [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void finalprocess() { System.Console.Out.Flush(); } // end of func } // end of class /// 座標に便利(値型だけど16byteまではstructが速い) struct YX { public int y; public int x; public YX(int y, int x) { this.y = y; this.x = x; } } // end of class /// グラフをするときに(値型だけど16byteまではstructが速い) struct Edge { public int from; public int to; public long cost; public Edge(int from, int to, long cost) { this.from = from; this.to = to; this.cost = cost; } } // end of class class Kyopuro { static int ans = -2; public static void Main() { // preprocess(); var kyopuro = new Kyopuro(); kyopuro.Solve(); writeline(ans); // finalprocess(); } // end of func HashSet colored0 = new HashSet(); HashSet colored1 = new HashSet(); int h; int w; [MethodImpl(MethodImplOptions.AggressiveInlining)] bool check(int c) { bool f0 = false; bool f1 = false; int y = c / w; int x = c % w; int up = (y - 1) * w + x; int down = (y + 1) * w + x; int left = y * w + x - 1; int right = y * w + x + 1; if (colored0.Contains(up) || colored0.Contains(down) || colored0.Contains(left) || colored0.Contains(right)) f0 = true; if (colored1.Contains(up) || colored1.Contains(down) || colored1.Contains(left) || colored1.Contains(right)) f1 = true; return f0 & f1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Solve() { (h, w) = readintt2(); var masu = new int[h][]; for (int i = 0; i < h; ++i) { masu[i] = readints(); } // 01の隣接, 隣接マスの数値、座標 var pq0 = new MyPriorityQueue<(int, int)>((a, b) => a.Item1 <= b.Item1); var pq1 = new MyPriorityQueue<(int, int)>((a, b) => a.Item1 <= b.Item1); pq0.Enqueue((masu[0][0], 0)); pq1.Enqueue((masu[h - 1][w - 1], (h - 1) * w + w - 1)); while (true) { // 0のターン int y = -1, x = -1, coord = -1; while (pq0.Count > 0) { var (num, yx) = pq0.Dequeue(); // すでに塗っているとだめ if (colored0.Contains(yx) || colored1.Contains(yx)) continue; y = yx / w; x = yx % w; coord = yx; break; } if (coord == -1) return; // 塗る colored0.Add(coord); ans += 1; // 隣接確保, u,d,l,r int up = (y - 1) * w + x; int down = (y + 1) * w + x; int left = y * w + x - 1; int right = y * w + x + 1; if (colored1.Contains(up) || colored1.Contains(down) || colored1.Contains(left) || colored1.Contains(right)) return; if (0 < y) pq0.Enqueue((masu[y - 1][x], up)); if (y < h - 1) pq0.Enqueue((masu[y + 1][x], down)); if (0 < x) pq0.Enqueue((masu[y][x - 1], left)); if (x < w - 1) pq0.Enqueue((masu[y][x + 1], right)); // writeline($"0のターン y:{y} x:{x} masu:{masu[y][x]}"); y = -1; x = -1; coord = -1; while (pq1.Count > 0) { var (num, yx) = pq1.Dequeue(); // すでに塗っているとだめ if (colored0.Contains(yx) || colored1.Contains(yx)) continue; y = yx / w; x = yx % w; coord = yx; break; } if (coord == -1) return; // 塗る colored1.Add(coord); ans += 1; // 隣接確保, u,d,l,r up = (y - 1) * w + x; down = (y + 1) * w + x; left = y * w + x - 1; right = y * w + x + 1; if (colored0.Contains(up) || colored0.Contains(down) || colored0.Contains(left) || colored0.Contains(right)) return; if (0 < y) pq1.Enqueue((masu[y - 1][x], up)); if (y < h - 1) pq1.Enqueue((masu[y + 1][x], down)); if (0 < x) pq1.Enqueue((masu[y][x - 1], left)); if (x < w - 1) pq1.Enqueue((masu[y][x + 1], right)); // writeline($"1のターン y:{y} x:{x} masu:{masu[y][x]}"); } } // end of method } // end of class class MyPriorityQueue { /// 内部で持つヒープ配列 public List heap = new List(); /// 現在の要素数 public int Count { get { return heap.Count; } } /// 比較用関数 (第1引数の方が優先度が高いときにtrue) private Func Compare; public MyPriorityQueue(Func compare) { this.Compare = compare; } // end of constructor /// 新規の値を追加する [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Enqueue(T num) { // 追加する要素のノード番号  int node = this.heap.Count; this.heap.Add(num); // 可能な限り親と交換 while (node > 0) { // 親ノード int p = (node - 1) / 2; // 交換条件を満たさなくなったら終わり if (this.Compare(num, heap[p]) == false) break; // 親ノードの値を子に降ろす heap[node] = heap[p]; node = p; } // end of while // 新規の値を下ろす場所を見つけたので終わり heap[node] = num; } // end of method /// 一番優先度の高い値を返す [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Peek() => this.heap[0]; /// 一番優先度の高い値を返して削除する [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Dequeue() { // return用の優先度が一番高い値 T ret = this.heap[0]; // 先頭を削除 this.Pop(); return ret; } // end of method /// 一番優先度の高い値を削除する [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Pop() { // 根に持ってくる値 T last = heap[this.heap.Count - 1]; // 最後尾を削除 O(1) this.heap.RemoveAt(this.heap.Count - 1); // 要素がなくなったら終了 if (this.heap.Count == 0) return; // 先頭を置き換えて降ろしていく int node = 0; while (node * 2 + 1 < this.heap.Count) { int a = node * 2 + 1; int b = node * 2 + 2; // 右の子が存在して、なおかつ優先度が高いならば if (b < this.heap.Count && this.Compare(this.heap[b], this.heap[a])) a = b; // 交換条件を満たさなくなったら終わり if (this.Compare(last, this.heap[a])) break; // 優先度の高い子を上げる this.heap[node] = this.heap[a]; node = a; } // end of while // 先頭に持ってきた値の置き場所が決まったので更新 this.heap[node] = last; } // end of method } // end of class