赞
踩
(已经看过的可以直接跳到下一部分了)
B站视频:
最强大脑:B圈S圈层破圈突围赛,一对一挑战“黑白迭代”
黑白迭代游戏有以下几个重要性质:
BLACK
和WHITE
random.shuffle
了也没事)由空白盘面构造目标盘面,也可以简化为从目标盘面还原空白,两者操作顺序相同。
这时我们需要寻找一种只翻转某个特定方格,其余方格不变的公式。
设
X
(
a
,
b
)
X_{\left( a,b\right) }
X(a,b)为只翻转(a, b)位方格的解法。
上图中,将找到的
X
(
8
,
3
)
X_{\left( 8,3\right) }
X(8,3)与
X
(
8
,
4
)
X_{\left( 8,4\right) }
X(8,4)合并,即可消除。
根据黑白迭代的性质3,可根据偶消奇不消的规则将其整合成一个解法。
通过观察发现,这一规则实际上就是异或(XOR)
点击(0, 0)后,出现三个黑色点,如下图所示:
根据合并解法的方法,可列出如下等式:
X ( 0 , 0 ) ⊻ X ( 0 , 1 ) ⊻ X ( 1 , 0 ) = [ [ 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ] X_{\left( 0,0\right) } \veebar X_{\left( 0,1\right) } \veebar X_{\left( 1,0\right) } = [ [1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0] ] X(0,0)⊻X(0,1)⊻X(1,0)=[[1,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0]]
⊻ \veebar ⊻ 表示异或
所以,可列出64个异或方程:
X ( 0 , 0 ) ⊻ X ( 0 , 1 ) ⊻ X ( 1 , 0 ) = [ [ 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ] X_{\left( 0,0\right) } \veebar X_{\left( 0,1\right) } \veebar X_{\left( 1,0\right) } = [ [1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0] ] X(0,0)⊻X(0,1)⊻X(1,0)=[[1,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0]]
X ( 0 , 0 ) ⊻ X ( 0 , 1 ) ⊻ X ( 1 , 1 ) ⊻ ( 1 , 2 ) = [ [ 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ] X_{\left( 0,0\right) } \veebar X_{\left( 0,1\right) } \veebar X_{\left( 1,1\right) } \veebar {\left( 1,2\right) } = [ [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0] ] X(0,0)⊻X(0,1)⊻X(1,1)⊻(1,2)=[[0,1,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0]]
. . . ... ...
X ( 7 , 7 ) ⊻ X ( 7 , 6 ) ⊻ X ( 6 , 7 ) = [ [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 ] ] X_{\left( 7,7\right) } \veebar X_{\left( 7,6\right) } \veebar X_{\left( 6,7\right) } = [ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1] ] X(7,7)⊻X(7,6)⊻X(6,7)=[[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,1]]
用高斯消元法解这个方程组,得到答案。
高斯消元解异或方程组的方法不介绍了,这里分析具体算法。
由于64个未知数的解是一个矩阵,为了简化计算,可以将矩阵转化为数字。
例如,下面的矩阵:
[
[1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]
]
可以转变为如下二进制数,方便按位异或计算:
0b1000000000000000000000000000000000000000000000000000000000000000000000000000000000
解出X的值后,将整数转为二进制,不够64位前面补0,获得矩阵。
用pygame
简单写一个,用到的黑白方块:
将上图保存为black.bmp
和white.bmp
如下所示:
解法部分,如下:
from math import ceil from typing import List, Tuple # 定义各种类型标注 SolveType = List[Tuple[int, int]] BoardType = List[List[int]] EquationType = List[Tuple[List[Tuple[int, int]], Tuple[int, int]]] WIDTH = 8 # 格数 WHITE, BLACK = 0, 1 def get_blank_board(): # 获取一个空白盘面 return [[WHITE] * WIDTH for _ in range(WIDTH)] def get_periphery(x: int, y: int): # 求所有周边点,包括自己 res = [(x, y)] for tx, ty in ((0, 1), (1, 0), (0, -1), (-1, 0)): nx, ny = x + tx, y + ty if 0 <= nx < WIDTH and 0 <= ny < WIDTH: res.append((nx, ny)) return res def equation(): # 列异或方程组 res = [] for x in range(WIDTH): for y in range(WIDTH): periphery = get_periphery(x, y) res.append((periphery, (x, y))) return res def chunk(x: list, step: int): # 按步长切割列表x step = int(step) return list( map( lambda n: x[n * step: n * step + step], list(range(0, ceil(len(x) / step))) ) ) def gaussian(equ: EquationType): # 异或方程组转矩阵 matrix = [] # 增广矩阵 # 系数1 系数2 ... 系数64 等号右侧 for xs, pos in equ: p = [] for x, y in xs: p.append(x * WIDTH + y) a = [0] * (WIDTH * WIDTH) for i in p: a[i] = 1 # 系数只有1或0 a.append(array_to_int([pos])) # 右端常数 matrix.append(a) return matrix def array_to_int(array: SolveType): # 解法转十进制整数 board = get_blank_board() for x, y in array: board[x][y] = BLACK flatten = lambda x: [y for L in x for y in flatten(L)] if type(x) is list else [x] # 碾平二维列表 a = flatten(board) del flatten res = [0] * (WIDTH * WIDTH) for inx, item in enumerate(a): if item == BLACK: res[inx] = 1 res = ''.join(str(i) for i in res) # 矩阵转二进制 return int(res, base=2) # 二进制转整数 def int_to_array(n: int): # 十进制整数转解法 lst = list(bin(n).replace('0b', '')) lst = ['0'] * (WIDTH * WIDTH - len(lst)) + lst # 补0 board = [0] * (WIDTH * WIDTH) for inx, item in enumerate(lst): if item == '1': board[inx] = BLACK board = chunk(board, WIDTH) return board def guass(n: int, matrix: BoardType): # 高斯消元法解异或方程组 r = 0 for c in range(n): t = r for i in range(r, n): # 找1 if matrix[i][c] == 1: t = i break if matrix[t][c] == 0: continue matrix[r], matrix[t] = matrix[t], matrix[r] # 交换两行 for i in range(r + 1, n): # 1与r行异或 if matrix[i][c] == 1: for j in range(c, n + 1): matrix[i][j] ^= matrix[r][j] # 合并 r += 1 if r < n: # 这个BUG只在30阶出现 for i in range(r, n): if matrix[i][n] == 1: raise SystemExit('方程组无解') raise SystemExit('方程组有多组解') for i in range(n - 1, -1, -1): # 注意是倒序 for j in range(i): if matrix[j][i] == 1: matrix[j][n] ^= matrix[i][n] for i in range(n): yield matrix[i][n] def solve(): # 获取解方程的结果 m = gaussian(equation()) res = {} # 哈希表存储 for inx, num in enumerate(guass(WIDTH * WIDTH, m)): val = int_to_array(num) x, y = inx // WIDTH, inx % WIDTH # 一维转二维公式 res[(x, y)] = val return res def merge(a: BoardType, b: BoardType): # 合并两组解,实际上就是异或 if not a: return b if not b: return a res = get_blank_board() for x, i in enumerate(a): for y, j in enumerate(i): k = b[x][y] if k == j: # 异或核心 res[x][y] = WHITE else: res[x][y] = BLACK return res def get(board: BoardType): # 主函数 solution = solve() res = [] for x, i in enumerate(board): for y, j in enumerate(i): if j == BLACK: res.append(solution[(x, y)]) ans = [] for i in res: ans = merge(ans, i) # 合并解法,减少空间和可视化消耗 res = [] for x, i in enumerate(ans): for y, j in enumerate(i): if ans[x][y] == BLACK: res.append((x, y)) return res if __name__ == '__main__': test = [ [0, 0, 1, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 1, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1, 0, 0, 1, 0, 1], [0, 1, 1, 0, 0, 1, 1, 0], [1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1, 1, 1, 1, 0, 1] ] # 节目最难的第三题 print(get(test))
可视化部分,如下:
import sys import os import pygame from pygame.locals import * from solve import * BLACK_PATH = os.path.join('resources', 'black.bmp') # 黑色图路径 WHITE_PATH = os.path.join('resources', 'white.bmp') GRID_WIDTH = 40 SPEED = 5 # 显示速度,越高越慢 FPS = 60 # 帧率 def destroy(): pygame.quit() sys.exit(0) class Window(object): def __init__(self, solution: SolveType): pygame.init() # 必不可少 self.solution = solution self.screen = pygame.display.set_mode((GRID_WIDTH * WIDTH, GRID_WIDTH * WIDTH)) pygame.display.set_caption('黑白迭代可视化') self.board = get_blank_board() self.black = pygame.image.load(BLACK_PATH) self.white = pygame.image.load(WHITE_PATH) self.inx = 0 # 播放索引 self.clock = pygame.time.Clock() def draw_board(self): for r, i in enumerate(self.board): for c, j in enumerate(i): x, y = c * GRID_WIDTH, r * GRID_WIDTH # 特别注意:c和r不能反 if j == WHITE: self.screen.blit(self.white, (x, y)) if j == BLACK: self.screen.blit(self.black, (x, y)) def reverse(self, x: int, y: int): lst = [(x, y)] for tx, ty in ((0, 1), (1, 0), (0, -1), (-1, 0)): nx, ny = x + tx, y + ty if 0 <= nx < WIDTH and 0 <= ny < WIDTH: lst.append((nx, ny)) for nx, ny in lst: self.board[nx][ny] = WHITE if self.board[nx][ny] == BLACK else BLACK def show(self): rates = 0 while True: self.clock.tick(FPS) for event in pygame.event.get(): if event.type == QUIT: destroy() if rates % SPEED == 0: if self.inx >= len(self.solution): # 溢出,结束 break self.reverse(*self.solution[self.inx]) # 翻转 self.inx += 1 rates += 1 self.draw_board() pygame.display.update() while True: # 第二循环,显示复原结果 self.clock.tick(FPS) for event in pygame.event.get(): if event.type == QUIT: destroy() self.draw_board() pygame.display.update()
入口程序,如下:
from gui import Window from solve import get test = [ [0, 0, 1, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 1, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1, 0, 0, 1, 0, 1], [0, 1, 1, 0, 0, 1, 1, 0], [1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1, 1, 1, 1, 0, 1] ] # 节目最难的第三题 solution = get(test) Window(solution).show()
这里指解法部分。
设n为边长(阶数),高斯消元法时间复杂度
O
(
n
3
)
O(n^{3})
O(n3)
而一共要求解
n
2
n^{2}
n2个格子的解法,所以时间复杂度为
O
(
n
5
)
O(n^{5})
O(n5)。
在最坏情况下(全部变黑),需要 n 2 n^{2} n2的空间,空间复杂度 O ( n 2 ) O(n^{2}) O(n2)
最好情况 | 最坏情况 | |
---|---|---|
时间复杂度 | O ( n 5 ) O(n^{5}) O(n5) | O ( n 5 ) O(n^{5}) O(n5) |
空间复杂度 | O ( 1 ) O(1) O(1) | O ( n 2 ) O(n^{2}) O(n2) |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。