using System;
using System.Collections.Generic;
using System.Linq;
namespace HJ43
{
internal class Program
{
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point Father { get; set; }
public Point() { }
public Point(int x, int y)
{
X = x;
Y = y;
}
public Point(int x, int y, Point point)
{
X = x;
Y = y;
Father = point;
}
public override string ToString()
{
return $"({X},{Y})";
}
}
static void Main(string[] args)
{
var inputStr = Console.ReadLine();
var stringArr = inputStr.Split();
int m = int.Parse(stringArr[0]);
int n = int.Parse(stringArr[1]);
int[,] ints = new int[m, n];
for (int i = 0; i < m; i++)
{
var strs = Console.ReadLine().Split();
for (int j = 0; j < n; j++)
{
ints[i, j] = int.Parse(strs[j]);
}
}
bool[,] isVisited = new bool[m, n];
List<Point> path = new List<Point>();
//if (DeepthFirstSearch(ints, isVisited, 0, 0, path))
//{
// foreach (Point p in path)
// {
// Console.WriteLine(p);
// }
//}
Point finalPoint = BreadthFirstSearch(ints, isVisited, 0, 0, path);
if (finalPoint != null)
{
List<Point> points = new List<Point>();
points.Add(finalPoint);
Point father = finalPoint.Father;
while (true)
{
if (father == null)
{
break;
}
//从终点往起点方向加入路径点
points.Add(father);
father = father.Father;
}
//回为加入时是从终点加入,因此需要倒置输出
points.Reverse();
foreach (Point point in points)
{
Console.WriteLine(point);
}
}
}
/// <summary>
/// 深度优先搜索,从一个点一直往下层走,不断往树的叶的方向走
/// </summary>
static bool DeepthFirstSearch(int[,] map, bool[,] isVisited, int x, int y, List<Point> path)
{
path.Add(new Point(x, y));
while (true)
{
if (x == map.GetLength(0) - 1 && y == map.GetLength(1) - 1)
{
return true;
}
//假如向下能走并且下边的点没有被访问过,向下走
if (x + 1 < map.GetLength(0) && map[x + 1, y] == 0 && isVisited[x + 1, y] == false)
{
x = x + 1;
path.Add(new Point(x, y));
//从这个点开始下一步搜索,同时将这个点标记为访问过,防止下个点搜索后回到这个点
isVisited[x, y] = true;
continue;
}
//假如向右能走并且右边的点没有被访问过,向右走
if (y + 1 < map.GetLength(1) && map[x, y + 1] == 0 && isVisited[x, y + 1] == false)
{
y = y + 1;
path.Add(new Point(x, y));
//从这个点开始下一步搜索,同时将这个点标记为访问过,防止下个点搜索后回到这个点
isVisited[x, y] = true;
continue;
}
//假如向上能走并且上边的点没有被访问过,向上走
if (x - 1 >= 0 && map[x - 1, y] == 0 && isVisited[x - 1, y] == false)
{
x = x - 1;
path.Add(new Point(x, y));
//从这个点开始下一步搜索,同时将这个点标记为访问过,防止下个点搜索后回到这个点
isVisited[x, y] = true;
continue;
}
//假如向左能走并且左边的点没有被访问过,向左走
if (y - 1 >= 0 && map[x, y - 1] == 0 && isVisited[x, y - 1] == false)
{
y = y - 1;
path.Add(new Point(x, y));
//从这个点开始下一步搜索,同时将这个点标记为访问过,防止下个点搜索后回到这个点
isVisited[x, y] = true;
continue;
}
//上下左右都不能走时,回退一步,
//由于之前这条路径上的点都被标记访问过,因此不会再走这条路径
path.RemoveAt(path.Count - 1);
//如果多次深入搜索之后都没有路径,且最后回退到起始点,说明没用路径;
if (path.Count == 1)
{
return false;
}
x = path.Last().X;
y = path.Last().Y;
}
}
/// <summary>
/// 广度优先搜索,从树的一层层去搜索,搜索完成一层再下一层
/// </summary>
static Point BreadthFirstSearch(int[,] map, bool[,] isVisited, int x, int y, List<Point> path)
{
path.Add(new Point(x, y));
Point p = null;
while (true)
{
//无解,退出
if (path.Count == 0)
{
return null;
}
p = path[0];
path.RemoveAt(0);
x = p.X;
y = p.Y;
if (x == map.GetLength(0) - 1 && y == map.GetLength(1) - 1)
{
return p;
}
//假如向下能走并且下边的点没有被访问过,加入进来
if (x + 1 < map.GetLength(0) && map[x + 1, y] == 0 && isVisited[x + 1, y] == false)
{
path.Add(new Point(x + 1, y, p));
//将这个点标记为访问过,防止下个点搜索后再次加入这个点,保证每个点只被加入一次
isVisited[x + 1, y] = true;
}
//假如向右能走并且右边的点没有被访问过,加入进来
if (y + 1 < map.GetLength(1) && map[x, y + 1] == 0 && isVisited[x, y + 1] == false)
{
path.Add(new Point(x, y + 1, p));
//将这个点标记为访问过,防止下个点搜索后再次加入这个点,保证每个点只被加入一次
isVisited[x, y + 1] = true;
}
//假如向上能走并且上边的点没有被访问过,加入进来
if (x - 1 >= 0 && map[x - 1, y] == 0 && isVisited[x - 1, y] == false)
{
path.Add(new Point(x - 1, y, p));
//将这个点标记为访问过,防止下个点搜索后再次加入这个点,保证每个点只被加入一次
isVisited[x - 1, y] = true;
}
//假如向左能走并且左边的点没有被访问过,加入进来
if (y - 1 >= 0 && map[x, y - 1] == 0 && isVisited[x, y - 1] == false)
{
path.Add(new Point(x, y - 1, p));
//将这个点标记为访问过,防止下个点搜索后再次加入这个点,保证每个点只被加入一次
isVisited[x, y - 1] = true;
}
}
}
}
}