棋盘覆盖问题
在一个 2 k × 2 k 2^k×2^ k 2k×2k个方格组成的棋盘中,恰有一个方格与 其他方格不同,称该方格为一特殊方格,且称该棋盘 为一特殊棋盘。如图1所示,蓝色的为特殊方格:
棋盘覆盖问题是指,要用图2中的4种不同形态的L型 骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方 格,且任何2个L型骨牌不得重叠覆盖。
问题分析
从图2可以看出,在一个2*2的方格中,指定任意一个特殊格子,都可以用一种L形骨牌填充剩下的格子,也就是说,如果在任意一个 2 k × 2 k 2^k×2^ k 2k×2k大小的方格中,若最后都能变成2*2方格子,指定一个特殊格子的子问题,则最终一定可以解决。
一个 2 k × 2 k 2^k×2^ k 2k×2k大小的方格一定可以划分为四个 2 k − 1 × 2 k − 1 2^{k-1} × 2^{k-1} 2k−1×2k−1大小的方格,由于原来的方格中有一个为特殊方格,则划分后的四个区域中一定有一个含有特殊方格,其他三个不含有特殊方格,为了将子问题变成与父问题一样,我们需要将另外三个部分也变成含有一个特殊方格的部分,而L形骨牌中,可以填涂三个方格,既当我们填充划分后的剩余三个部分连接处的三个方格,刚好能将各个部分都变成含有一个特殊格子的区域,如下图所示。
按照此思路,一直往下划分,最后划分至 2 ∗ 2 2 * 2 2∗2大小的方格时,可以直接填充。
算法设计
代码实现
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 判断一个点是否在指定范围内
def isInChessboard(k: int, startSite: (int, int), site: (int, int)):
return startSite[0] <= site[0] < startSite[0] + pow(2, k) and startSite[1] <= site[1] < startSite[1] + pow(2, k)
"""
k: 棋盘大小(2^k * 2^k)
num: 当前填充的L形骨牌编号
startSite: 当前棋盘的起始坐标
site,特殊方块的坐标
返回值为当前棋盘填充后的骨牌号
"""
def chessboardCross(k: int, num: int, startSite: (int, int), site: (int, int)) -> int:
if k == 1:
# 2 * 2的方格,将除了特殊方格外的其他方格填充
for i in range(startSite[0], startSite[0] + 2):
for j in range(startSite[1], startSite[1] + 2):
if i != site[0] or j != site[1]:
chessboard[i][j] = num # 填涂
return num + 1
# 将方块一分为4,界限进行填涂,除了含有特殊方块的其他三个部分分解处将会被填涂
center_i, center_j = startSite[0] + pow(2, k - 1) - 1, startSite[1] + pow(2, k - 1) - 1
newNum = num + 1
# 第一个区域
if isInChessboard(k - 1, startSite, site):
newNum = chessboardCross(k - 1, newNum, startSite, site)
else:
# 填涂边缘区域
chessboard[center_i][center_j] = num
newNum = chessboardCross(k - 1, newNum, startSite, (center_i, center_j))
# 第二个区域
if isInChessboard(k - 1, (startSite[0], center_j + 1), site):
newNum = chessboardCross(k - 1, newNum, (startSite[0], center_j + 1), site)
else:
chessboard[center_i][center_j + 1] = num
newNum = chessboardCross(k - 1, newNum, (startSite[0], center_j + 1), (center_i, center_j + 1))
# 第三个区域
if isInChessboard(k - 1, (center_i + 1, startSite[1]), site):
newNum = chessboardCross(k - 1, newNum, (center_i + 1, startSite[1]), site)
else:
chessboard[center_i + 1][center_j] = num
newNum = chessboardCross(k - 1, newNum, (center_i + 1, startSite[1]), (center_i + 1, center_j))
# 第四个区域
if isInChessboard(k - 1, (center_i + 1, center_j + 1), site):
newNum = chessboardCross(k - 1, newNum, (center_i + 1, center_j + 1), site)
else:
chessboard[center_i + 1][center_j + 1] = num
newNum = chessboardCross(k - 1, newNum, (center_i + 1, center_j + 1), (center_i + 1, center_j + 1))
return newNum
chessboard = np.zeros((pow(2, 3), pow(2, 3)))
chessboardCross(3, 2, (0, 0), (3, 3))
ax = sns.heatmap(chessboard)
plt.show()
结果展示
其中值为0,图为黑色的方块为特殊方块,同颜色的表示为一个L形骨牌。