给定一个mxn的矩阵,由若干字符'X'和'O'构成,'X'表示该处已被占据,'O'表示该处空闲,请找到最大的单入口空闲区域
解释:
空闲区域是由连通的'O'组成的区域,位于边界的'O'可以构成入口.单入口空闲区域即有且只有一个位于边界的'O'作为入口的由连通的'O'组成的区域.如果两个元素在水平或垂直方向相邻,则称它们是“连通”的
输入描述
第一行输入为两个数字,第一个数字为行数 m,第二个数字为列数 n,两个数字以空格分隔,
1<=m,n<=200.
剩余各行为矩阵各行元素,元素为'X'或'O',各元素间以空格分隔
输出描述
若有唯一符合要求的最大单入口空闲区域,输出三个数字
第一个数字为入口行坐标(0~m-1)
第二个数字为入口列坐标(0~n-1)
第三个数字为区域大小
三个数字以空格分隔;
若有多个符合要求,则输出区域大小最大的,若多个符合要求的单入口区域的区域大小相同,
则此时只需要输出区域大小,不需要输出入口坐标
若没有,输出 NULL
用例
输出 | 输出 |
---|---|
4 4 X X X X X O O X X O O X X O X X |
3 1 5 |
思考一(DFS)
矩阵搜索问题,可以通过 DFS 或 BFS 解决。先看 DFS,要求统计单入口区域大小,那么可以围绕着矩阵边界寻找元素为‘O'的位置作为起点进行深度优先搜索。每个入口表示一次 DFS,过程中我们要统计区域大小,即元素 ’O' 的数量,怎么把这个数量和当前 DFS 进行关联呢?由于一般 DFS 是递归实现,这里也是采用递归实现 DFS 过程,因此我们每递归调用一次 dfs 函数就会开启一个临时栈,这个栈在结束时会销毁内部关联的数据,可以通过引用参数把函数栈的计算数据保存起来。也就是说可以在DFS起点的时候就传递一个对象或数组存放当前 DFS 起点坐标(及入口坐标)、区域大小 count 初始化参数值为 0,然后搜索过程中不断更新这个对象的属性值。当我们搜索到另一个位于边界上的元素'O'时,表明当前 DFS 搜索已经包含了两个入口,显然不满足单入口要求,需要丢弃当前搜索结果,终止本次 DFS,但并不能立即终止递归。递归搜索是分叉的树状结构,在某个分支节点上遇到了该结束的状态,但是其它分支依然在进行展开,我们没法立即通知它们完结销毁整棵递归树 。 这时候需要把那个引用传递给 dfs 函数的对象参数更新下,比如把 count 置为 -1,在递归函数中判断如果 count 是 -1 就回溯,因为引用传递的参数对象是共享的,因此所有递归调用的函数栈中都能看到相同的状态,都立即回溯,最终整棵递归树就销毁了。如果搜索整个过程顺利没有遇到第二个入口,那么最终这次 DFS 的统计的区域大小 count 和 起点坐标 x,y 都要与全局的结果进行比较更新,保存最大的count,已存在相同大小的 count 就更新全局变量 isMult 为 true 表示存在多个相同大小的单入口区域。但实际实现感觉这种通过函数的引用参数保存搜索结果有时候处理逻辑写起来并不是很容易且直观。所以还有一种通过全局的字典变量来记录每次 DFS 搜索结果更简单些,就像记录元素是否被访问过的备忘录变量 visited数组。用入口的位置坐标以空格作为分割拼接字符串作为字典的 key,JavaScript 就用 Map 来处理,用区域大小 count 作为字典的值。这样 dfs函数只要传递当前元素位置坐标 和 起始位置[x,y] 就