华为机试 查找单入口空闲区域

2025/04/12

题目描述

给定一个m * n的矩阵一,由若干字符X和O构成,X表示该处已被占据,О表示该处空闲,请找到最大的单入口空闲区域。 空闲区域是由连通的О组成的区域,位于边界的О可以构成入口,单入口空闲区域即有且只有一个位于边界的О作为入口的由连通的О组成的区域。 如果两个元素在水平或垂直方向相邻,则称它们是“连通”的。

输入描述

第一行输入为两个数字: 第一个数字为行数m,第二个数字为列数n,两个数字以空格分隔,1 ≤m,n ≤200; 剩余各行为矩阵各行元素,元素为X或O,各元素间以空格分隔。

输出描述

若有唯一符合要求的最大单入口空闲区域,输出三个数字: 第一个数字为入口行坐标(范围为0~行数-1); 第二个数字为入口列坐标(范围为0~列数-1) ; 第三个数字为区域大小,三个数字以空格分隔; 若有多个符合要求的最大单入口空闲区域,输出一个数字,代表区域的大小; 若没有,输出NULL。 示例 1

输入:

4 4 
X X X X 
X O O X 
X O O X 
X O X X 

输出:

3 1 5 

示例 2

输入:

4 5 
X X X X X 
O O O O X 
X O O O X 
X O X X O 

输出:

3 4 1  

示例 3

输入:

5 4 
X X X X 
X O O O 
X O O O 
X O O X 
X X X X 

输出:

NULL 

说明: 只有一个空闲区域,但是空闲区域有两个入口,所有输出NULL

示例 4

输入:

5 4 
X X X X 
X O O O 
X X X X 
X O O O 
X X X X 

输出:

3 

说明: 有两个空闲区域,入口坐标分别四 1,3 和 3,3 ,空闲区域大小都是3,所以输出3。 ‘’’ ‘’’ 思路: 使用并查集将当前的O点和上一列和上一行的O点连接 并维护每个点所在连通块的三个信息 1.每个点所在的连通块的id,连通块id是从按顺序的第一个元素的id 2.每个连通块中位于边界上的点的数量 3.每个连通块的边界点的坐标 ‘’’

n,m=[int(x) for x in input().split()]
g=[]
for i in range(n):  g.append(input().split())
# 每个点所在连通块id
q=[0]*(n*m)
# 每个点默认单独是一个连通块,id是字符从上到下从左到右数的序号
for i in range(n*m): q[i]=i
# cnt 每个点所在连通块的元素数量 cnt2 每个点所在连通块中边界元素的数量 cnt3 每个联通块的边界点的坐标
cnt,cnt2,cnt3=[1]*(n*m),[0]*(n*m),{}

# 查找每个点所在连通块id
def find(x):
    if q[x]!=x: q[x]=find(q[x])
    return q[x]

# 检查一个点是否在边界
def check(i,j):
    return i==0 or i==n-1 or j==0 or j==m-1

# 如果ab不在同一个联通块,**将a合入到b的连通块中
def merge(a,b):
    fa,fb=find(a),find(b)
    if fa==fb: return
    # 合入的点a是否是边界点
    if check(a//m,a%m):
        cnt2[fb]+=1  #更新b所在连通块的边界点的数量
        # 如果只有一个边界点的话,才记录下当前连通块的边界点
        if cnt2[fb]==1: cnt3[fb]=[a//m,a%m]

    q[fa]=fb # 将a加入到b的连通块中
    cnt[fb]+=1 # 更新b所在连通块的元素数量

for i in range(n):
    for j in range(m):
        if g[i][j]=='X': continue
        if i>0 and g[i-1][j]=='O': merge(i*m+j,(i-1)*m+j)
        if j>0 and g[i][j-1]=='O': merge(i*m+j,i*m+j-1)
# 所有边界只有一个的连通块的 [元素数量,连通块id]信息
ret=[]
for i in range(n):
    for j in range(m):
        # 获取到当前点的连通块id
        ind=find(i*m+j)
        if g[i][j]=='O' and ind==i*m+j:
            # 上面在合入到连通块中时,没有检查第一个元素是否是边界元素,此处进行检查
            # 如果是,同样更新连通块的边界元素的数量
            if check(ind//m,ind%m):  cnt2[ind]+=1
            # 如果连通块只有一个边界元素的话,加入到结果中
            if cnt2[ind]==1: ret.append([cnt[ind],ind])
ret.sort(reverse=True) # 按照连通块的元素数量降序排序
if len(ret)==0: print('NULL')
# 只有一个连通块或者有两个元素数量不同的,则取第一个作为答案
elif len(ret)==1 or ret[0][0]>ret[1][0]: 
    c,ind=ret[0]
    x,y=cnt3[ind]
    print(x,y,c)
# 有多个连通块,但是元素个数相同,则输出符合条件的连通块的数量
elif ret[0][0]==ret[1][0]:
    print(ret[0][0])

Post Directory