棋盘覆盖问题

棋盘覆盖问题

在一个 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} 2k1×2k1大小的方格,由于原来的方格中有一个为特殊方格,则划分后的四个区域中一定有一个含有特殊方格,其他三个不含有特殊方格,为了将子问题变成与父问题一样,我们需要将另外三个部分也变成含有一个特殊方格的部分,而L形骨牌中,可以填涂三个方格,既当我们填充划分后的剩余三个部分连接处的三个方格,刚好能将各个部分都变成含有一个特殊格子的区域,如下图所示。

请添加图片描述

按照此思路,一直往下划分,最后划分至 2 ∗ 2 2 * 2 22大小的方格时,可以直接填充。

算法设计

请添加图片描述

代码实现

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形骨牌。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值