Tromino谜题
问题描述
Tromino是指一个由棋盘上的三个方块组成的L型骨牌。如何用Tromino覆盖一个缺少了一个方块(可以在棋盘上任何位置)2n × 2n的棋盘(下图展示了情况)。除了这个缺失的方块,Tromino应该覆盖棋盘上的所有方块,Tromino可以任意转向但不能有重叠。
整个棋盘一共有2n × 2n即4n 个格子,除去缺失的洞之外,还剩下4n -1个格子。由数学归纳法可证,4ⁿ -1为3的倍数,所以有可能存在解法。
现将棋盘由两条中线划分为等大的4份,除去原有的洞所在的一份之外,其他三份的格子数为4n-1,需要各自挖一个新的洞使得解存在,所以在三份的相邻处,也就是原棋盘的中心位置,添上一个L型骨牌,使得原问题可以划分为更小的子问题。 这利用了分治法的思想,每次分治找出中心处L型骨牌的摆放位置,直到剩下1×1大小的子棋盘结束分治。
算法描述
记录棋盘为一个2n×2n 的二维数组“board = zeros((2n,2n))”,洞及它的坐标表示为“hole(i,j)”,并将上述分析过程和解决的思路进一步归纳为以下步骤:
(1)找到当前棋盘的中心位置,并判断该棋盘上洞的位置,以此为依据,划分棋盘为4份。
(2)除去原有的洞所在的一份之外,在其他三份的相邻处,也就是原棋盘的中心位置,添上一个L型骨牌,在每一份棋盘中根据洞的位置,继续划分。
(3)重复第(1)(2)步,直到棋盘大小为1×1结束递归。
复杂度分析
设问题规模为2^n×2^n,每次递归调用函数会执行(2^n×2^n)次递归停止判断,(1+4+4×4+...+4^(n-1))一共(4^(n-1))/3次递归,每次递归需要执行1~4次洞的位置判断(取决于缺失方块的位置,而缺失方块的位置是随机的),所以本算法的时间复杂度为4^n+(4^(n-1))/3*(1+2+3+4)/4,即θ(4^n)。
由于使用了递归,系统需要使用堆栈保存上一次递归的情况直到合并阶段,所以它的空间复杂度为4n+(1+4+4×4+...+4n-1),即θ(4^n)。
黄色点为原始缺失的方块,递归时用随机的颜色填充时子棋盘的中心。
可以从图2.1中看到,如果将将该算法以决策树的形式呈现,递归顺序可以形容为对递归树的深度优先遍历。
代码:
import random
import numpy as np
import matplotlib.pyplot as plt
def tromino(pos_1, pos_2, hole):
#递归停止条件
if pos_1 == pos_2:
return
global board
#随机颜色
t = random.randint(0, 220)
plt.clf()
plt.imshow(board)
plt.pause(0.02)
#棋盘中心 偏向左上方
center = (((pos_2[0]+pos_1[0])//2), ((pos_2[1]+pos_1[1])//2))
#hole在左上方
if hole[0]<=center[0] and hole[1]<=center[1]:
board[center[0]][center[1]+1] = board[center[0]+1][center[1]] \
= board[center[0]+1][center[1]+1] = t
tromino(pos_1, center, hole)
tromino((pos_1[0],center[1]+1), (center[0],pos_2[1]), (center[0], center[1]+1))
tromino((center[0]+1,pos_1[1]), (pos_2[0],center[1]), (center[0]+1, center[1]))
tromino((center[0]+1,center[1]+1), pos_2, (center[0]+1, center[1]+1))
#hole在右上方
elif hole[0]<=center[0] and hole[1]>center[1]:
board[center[0]][center[1]] = board[center[0]+1][center[1]] \
= board[center[0]+1][center[1]+1] = t
tromino(pos_1, center, (center[0], center[1]))
tromino((pos_1[0],center[1]+1), (center[0],pos_2[1]), hole)
tromino((center[0]+1,pos_1[1]), (pos_2[0],center[1]), (center[0]+1, center[1]))
tromino((center[0]+1,center[1]+1), pos_2, (center[0]+1, center[1]+1))
#hole在左下方
elif hole[0]>center[0] and hole[1]<=center[1]:
board[center[0]][center[1]] = board[center[0]][center[1]+1] \
= board[center[0]+1][center[1]+1] = t
tromino(pos_1, center, (center[0], center[1]))
tromino((pos_1[0],center[1]+1), (center[0],pos_2[1]), (center[0], center[1]+1))
tromino((center[0]+1,pos_1[1]), (pos_2[0],center[1]), hole)
tromino((center[0]+1,center[1]+1), pos_2, (center[0]+1, center[1]+1))
#hole在右下方
elif hole[0]>center[0] and hole[1]>center[1]:
board[center[0]][center[1]] = board[center[0]+1][center[1]] \
= board[center[0]][center[1]+1] = t
tromino(pos_1, center, (center[0], center[1]))
tromino((pos_1[0],center[1]+1), (center[0],pos_2[1]), (center[0], center[1]+1))
tromino((center[0]+1,pos_1[1]), (pos_2[0],center[1]), (center[0]+1, center[1]))
tromino((center[0]+1,center[1]+1), pos_2, hole)
if __name__ == '__main__':
n = int(input('请输入规模:'))
n = 2**n
board = np.zeros((n,n))
hole = (random.randint(0,n-1), random.randint(0,n-1))
board[hole[0]][hole[1]] = 255
fig = plt.figure()
tromino((0,0), (n-1,n-1), hole)
plt.imshow(board)
plt.show()