算法笔记:带障碍的铺砖问题

问题描述:给定一个空间,用矩阵P表示,1表示有障碍,0表示没有,用长度为1x2的砖去铺满没有障碍的区域,砖可以横着或者竖着,但不能切割。

这道题一开始想了很久,一直想用带有回溯的方式去搜索所有可能性。但非常复杂,水平有限没写出来。后来想到了一个染色的方法,用两种颜色去染空白区域,比如黑白,一黑一白即为一块砖,并且同样颜色的砖块不能相邻。也就是说白色四周(上下左右)只能是黑,反之亦然。

看下面几个例子,画图的时候黑色区域表示障碍,0和1分别是两种颜色。

算法笔记:带障碍的铺砖问题_第1张图片
算法笔记:带障碍的铺砖问题_第2张图片
最后只需要数两种颜色的个数是否想等即可(这里把障碍用黑色表示,两种颜色用01表示)。

当然这只是所有空间连通的情况。不连通的也是可以判断的:
算法笔记:带障碍的铺砖问题_第3张图片
针对两个空白区域分开判断,而如果判断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,为下一次的空白区域做准备。

当前代码的测试结果如下:
算法笔记:带障碍的铺砖问题_第4张图片

可以看到正好三处空间,执行了三次判断,并且都为True。同时还打印了最后染色结束后的矩阵P。

该方法跳过了考虑砖块到底是如何摆放的。只要0和1个数相等,他们可以自动配对。

时间复杂度:
DFS算法,最多遍历NM个位置。复杂度O(NM)

你可能感兴趣的:(算法)