1. 问题描述:
如下图, 有12张连在一起的12生肖的邮票。现在你要从中剪下5张来,要求必须是连着的。(仅仅连接一个角不算相连)
比如,下面两张图中,粉红色所示部分就是合格的剪取
请你计算,一共有多少种不同的剪取方法。
输出
请填写表示方案数目的整数。
2. 思路分析:
① 因为要求必须剪的五张邮票数连接在一起的,所以根据这个特点我们可以知道可以使用dfs搜索进行五个方块的连通,一开始的想法是从当前的位置(i,j)出发,其中i,j表示二维坐标对应的位置,从当前的位置出发开始dfs搜索,每搜索一个位置那么就标记当前的(i, j)位置但是后面发现有的情况是漏掉了的,比如从第三行的数字9开始,因为之前已经搜索过第一行与第二行了所以这个时候以9开头的邮票是不能够剪成功的(最多存在9,10,11,12四块)而比如以9开始的1,2,3,5,9就是一个合法的剪法,所以上面这样标记是会忽略一些情况的,所以每一次从起点开始dfs搜索结束之后标记当前二维坐标位置已经访问是错误的解法
② 在网上看了一下具体的代码,发现正确的解法是从12个数字中选择出5个数字,然后记录下选择的5个数字,将5个数字映射到二维平面对应的坐标上,通过另外一个dfs2方法检验选择的5个数字是否连通,思路其实还是很好理解的。总的来说是使用递归生成12选5的数字,可以使用递归的方法来生成12个数字选5个数字的组合方式,在生成5个数字的过程中需要使用一个列表记录下这5个数字,并且将5个数字对应的坐标映射在二维平面上,然后再一次使用dfs检查连通性,答案是116
3. 代码如下:
组合 + 连通性判断:
from typing import List
res = 0
# count用来记录12选5个数字的下标
count = 0
# n表示
n = 0
A = [0] * 5
pos = [[0, 1], [0, -1], [-1, 0], [1, 0]]
# 检验5个数字在二维平面上的的连通性
def dfs2(x: int, y: int, B: List[List[int]]):
global n
for i in range(4):
x0, y0 = x + pos[i][0], y + pos[i][1]
if 0 <= x0 < 3 and 0 <= y0 < 4 and B[x0][y0] == 1:
B[x0][y0] = 0
n += 1
dfs2(x0, y0, B)
# 不知道为什么回溯之后结果会不正确
# B[x0][y0] = 1
def dfs(index: int):
global count
if count == 5:
# print(A)
# 注意在这里需要使用global来修改全局变量如果没有使用这个关键字会导致修改不了
global res, n
x, y = 0, 0
B = [[0] * 4 for i in range(3)]
for i in range(5):
x0, y0 = (A[i] - 1) // 4, (A[i] - 1) % 4
if i < 4:
# 标记矩阵中选择5个数字的二维坐标
B[x0][y0] = 1
else:
# 从最后一个坐标开始搜索
x, y = x0, y0
n = 1
dfs2(x, y, B)
# 一开始的计数为1
if n == 5:
res += 1
else:
for i in range(index + 1, 13):
A[count] = i
count += 1
dfs(i)
count -= 1
if __name__ == '__main__':
dfs(0)
print(res)
错误代码:
from typing import List
res = 0
pos = [[0, 1], [0, -1], [1, 0], [-1, 0]]
# rec用来记录中间的递归结果看输出结果是否正确
def dfs(x: int, y: int, count: int, vis: List[List[int]], rec: List[List[int]]):
global res
if count == 4:
for i in range(len(rec)):
print(rec[i])
print()
res += 1
for i in range(4):
x0, y0 = x + pos[i][0], y + pos[i][1]
# 可以到达当前的位置
if 0 <= x0 < 3 and 0 <= y0 < 4 and vis[x0][y0] == 0:
rec[x0][y0] = 1
vis[x0][y0] = 1
dfs(x0, y0, count + 1, vis, rec)
# 回溯
rec[x0][y0] = 0
vis[x0][y0] = 0
if __name__ == '__main__':
vis = [[0] * 4 for i in range(3)]
rec = [[0] * 4 for i in range(3)]
for i in range(3):
for j in range(4):
rec[i][j] = 1
vis[i][j] = 1
dfs(i, j, 0, vis, rec)
# 标记已经访问过的位置这样可以避免重复计算某些剪法
vis[i][j] = 1
rec[i][j] = 0
print(res)