问题描述:给定一个空间,用矩阵P表示,1表示有障碍,0表示没有,用长度为1x2的砖去铺满没有障碍的区域,砖可以横着或者竖着,但不能切割。
这道题一开始想了很久,一直想用带有回溯的方式去搜索所有可能性。但非常复杂,水平有限没写出来。后来想到了一个染色的方法,用两种颜色去染空白区域,比如黑白,一黑一白即为一块砖,并且同样颜色的砖块不能相邻。也就是说白色四周(上下左右)只能是黑,反之亦然。
看下面几个例子,画图的时候黑色区域表示障碍,0和1分别是两种颜色。
最后只需要数两种颜色的个数是否想等即可(这里把障碍用黑色表示,两种颜色用01表示)。
当然这只是所有空间连通的情况。不连通的也是可以判断的:
针对两个空白区域分开判断,而如果判断0和1 的总数的话,他们是想等的。这里是一个需要注意的地方。这种分开判断的思想参考了《leetcode 200 岛屿数量》这道题的部分思想。即我的搜索算法执行次数 等于 分开的空白区域 的个数。
并且这种朝四个方向染色的思想,跟岛屿数量那道题也挺像。
代码实现:
import numpy as np
dx=[1,0,0,-1]
dy=[0,1,-1,0]
# down, right,left, up
total1=0
total2=0
P=np.array(
[[1,0,0,0,0,1,1,0],
[1,1,1,1,1,1,0,0],
[0,0,0,0,1,0,0,0],
[1,0,0,1,1,0,1,0]] )*2
N = len(P)
M = len(P[0])
st = (P==2) #visited
st_sum = (P==2).sum()
def paint(i, j, k):
global total1
global total2
global st_sum
if i<0 or i>=N or j<0 or j>=M or P[i][j]==2 or st[i][j]==True:
return
P[i][j]=k
st[i][j]=True
st_sum+=1
if P[i][j]==0:
total1+=1
paint(i+1, j, 1)
paint(i-1, j, 1)
paint(i, j+1, 1)
paint(i, j-1, 1)
else:
total2+=1
paint(i+1, j, 0)
paint(i-1, j, 0)
paint(i, j+1, 0)
paint(i, j-1, 0)
res=True
for i in range(N):
for j in range(M):
if st[i][j]==False:
paint(i, j, P[i][j])
sub = (total1==total2)
print("current space: ", sub)
res = res and sub
total1=0
total2=0
if st_sum==N*M:
break
print(P)
print("final result: ", res)
代码解释:
我把所有的障碍标记成2,这样就可以和0,1区分开了,并且每次访问过的位置标记为True,这样就不会重复搜索。st_num用来记录访问的个数,所有障碍都是默认为访问过,当st_num等于矩阵大小时,即可直接结束。
total1和total2分别是记录0和1的个数,当for 循环中的当前paint()执行完之后,比较0和1的个数是否想等,然后和当前结果取和运算,最后重置记数器为0,为下一次的空白区域做准备。
可以看到正好三处空间,执行了三次判断,并且都为True。同时还打印了最后染色结束后的矩阵P。
该方法跳过了考虑砖块到底是如何摆放的。只要0和1个数相等,他们可以自动配对。
时间复杂度:
DFS算法,最多遍历NM个位置。复杂度O(NM)