蓝桥杯剪邮票(递归组合 + 连通性判断)

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)

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值