【OD机试题多种解法笔记】查找单入口空闲区域

大厂机试题:查找单入口空闲区域的解法

给定一个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] 就

### 华为OD模式下查找单入口空闲区域的实现方案 #### 问题分析 在给定的二维矩阵中,目标是找到满足条件的最大单入口空闲区域。这里的“单入口”意味着该区域仅有一个外部连接点作为进入路径。“空闲区域”是指由字符`O`表示的连续单元格组成的连通域。 为了高效解决此问题,可以采用深度优先搜索(DFS)或广度优先搜索(BFS)。以下是基于DFS的具体实现方法: --- #### 解决方案设计 1. **输入解析** 输入是一个二维数组,其中`X`代表障碍物,而`O`代表可通行的空间。需要遍历整个矩阵来寻找符合条件的区域。 2. **标记已访问节点** 使用辅助布尔型二维数组记录哪些位置已经被访问过,防止重复计算同一区域。 3. **定义单入口条件** 对于每个可能成为入口的位置(即边界的`O`),检查其周围是否有且只有一个相邻的`O`位于边界外侧。如果满足,则将其视为合法入口。 4. **执行DFS/BFS探索** 当发现有效入口时,启动DFS递归函数或者队列形式的BFS迭代过程,统计当前联通分量内的所有可达`O`的数量总和即为此区域面积大小;同时更新全局最优解数据结构存储最佳结果信息包括起始行列索引以及对应尺寸数值等属性值。 5. **返回最终答案** 遍历完成后输出具有最大规模的有效单开口开放空间详情描述字符串格式化表达式即可完成任务需求解答工作流程概述如下图所示[^1]: ```java import java.util.*; public class SingleEntryAreaFinder { private static final char EMPTY = 'O'; private static final char BLOCKED = 'X'; public String findMaxSingleEntryArea(char[][] grid) { int rows = grid.length; if (rows == 0) return "NULL"; int cols = grid[0].length; boolean[][] visited = new boolean[rows][cols]; List<int[]> candidates = getCandidateEntries(grid); int maxRow = -1, maxCol = -1, maxSize = 0; for(int[] candidate : candidates){ int row = candidate[0], col = candidate[1]; if(!visited[row][col]){ int size = dfs(row,col,grid,visited); if(size > maxSize){ maxRow = row; maxCol = col; maxSize = size; } } } if(maxSize == 0 || maxSize ==1 && !isProperEntrance(maxRow,maxCol,grid)){ return "NULL"; }else{ return maxRow+" "+maxCol+" "+maxSize; } } private List<int[]> getCandidateEntries(char[][] grid){ List<int[]> list=new ArrayList<>(); int m=grid.length,n=grid[0].length; // Check first and last columns for(int i=0;i<m;i++){ if(grid[i][0]==EMPTY&&!hasMoreThanOneNeighbor(i,0,grid))list.add(new int[]{i,0}); if(grid[i][n-1]==EMPTY&&!hasMoreThanOneNeighbor(i,n-1,grid))list.add(new int[]{i,n-1}); } //Check top and bottom rows excluding corners since they were already checked above. for(int j=1;j<n-1;j++) { if(grid[0][j]==EMPTY&&!hasMoreThanOneNeighbor(0,j,grid))list.add(new int[]{0,j}); if(grid[m-1][j]==EMPTY&&!hasMoreThanOneNeighbor(m-1,j,grid))list.add(new int[]{m-1,j}); } return list; } private boolean hasMoreThanOneNeighbor(int r,int c,char[][] g){ int count=0; int directions[][]={{-1,0},{1,0},{0,-1},{0,1}}; for(int d[]:directions){ int nr=r+d[0];int nc=c+d[1]; if(isInBounds(nr,nc,g)&&g[nr][nc]==EMPTY)count++; if(count>1)return true; } return false; } private boolean isInBounds(int r,int c,char [][]g){ return r>=0&&r<g.length&&c>=0&&c<g[0].length; } private int dfs(int r,int c,char[][] g,boolean[][] v){ Stack<int[]> stack=new Stack<>(); Set<String> seen=new HashSet<>(); stack.push(new int[]{r,c}); int area=0; while(!stack.isEmpty()){ int current[]=stack.pop(); int cr=current[0];int cc=current[1]; String key=cr+","+cc; if(seen.contains(key)||!isValid(cr,cc,g,v))continue; seen.add(key);v[cr][cc]=true;area++; addNeighborsToStack(stack,cr,cc,g,v); } return area; } private void addNeighborsToStack(Stack<int[]> s,int r,int c,char[][] g,boolean[][] v){ int dirs[][]={{-1,0},{1,0},{0,-1},{0,1}}; for(int dir[]:dirs){ int nr=r+dir[0];int nc=c+dir[1]; if(isValid(nr,nc,g,v))s.push(new int[]{nr,nc}); } } private boolean isValid(int r,int c,char[][] g,boolean[][] v){ return r>=0&&r<g.length&&c>=0&&c<g[0].length&&g[r][c]==EMPTY&&!v[r][c]; } private boolean isProperEntrance(int r,int c,char[][] g){ int neighbors=0; int dirs[][]={{-1,0},{1,0},{0,-1},{0,1}}; for(int []d:dirs){ int nr=r+d[0];int nc=c+d[1]; if(isInBounds(nr,nc,g)&&(g[nr][nc]==EMPTY||onBorder(nr,nc,g)))neighbors++; if(neighbors>1)return false; } return onBorder(r,c,g)?neighbors==1:false; } private boolean onBorder(int r,int c,char[][] g){ return r==0||r==g.length-1||c==0||c==g[0].length-1; } } ``` --- #### 关键逻辑解释 - `findMaxSingleEntryArea`: 主函数负责初始化并调用其他帮助函数找出最大的单一入口区域。 - `getCandidateEntries`: 找到潜在的入口候选者列表。 - `dfs`: 利用栈模拟递归来测量从某个起点出发能到达多少个相连的'O'。 - 边界处理与合法性验证确保只考虑真正的单入口情况。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值