方格填数(dfs)

1. 问题描述:

格填数
如下的10个格子
   +--+--+--+
   |  |  |  |
+--+--+--+--+
|  |  |  |  |
+--+--+--+--+
|  |  |  |
+--+--+--+
(如果显示有问题,也可以参看【图1.jpg】)填入0~9的数字。要求:连续的两个数字不能相邻。(左右、上下、对角都算相邻)一共有多少种可能的填数方案?


请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

2. 思路分析:

① 分析可以知道我们需要将0-9这个数字填入到如图所示的方格中,并且需要满足左右/上下/对角方向上不相邻,所以我们需要尝试所有的可能才能够知道最终满足条件的数目,很明显可以使用dfs解决,dfs能够搜索所有的可能性。对于这种方格填数字的问题一般使用的是在for循环中进行递归,这样我们就可以对当前的(x, y)位置尝试填入0-9这个数字,检查当前填入的数字是否存在冲突如果存在冲突那么尝试其他的数字进行填入。因为使用的是python语言,所以在一开始的时候可以声明长度为10的列表来记录之前已经使用过哪些数字,对于使用过的数字我们标记为1,这样下一次尝试填入数字的时候就可以知道当前尝试填入的数字是否与存在重复,如果之前使用过这个数字那么需要尝试其他的数字,如果没有使用过这个数字那么就需要检查是否存在冲突,因为方向是相互的,所以对于当前填入数字的对应位置只需要检查左边/上边/左上角/右上角的位置是否存在冲突即可,因为是需要检查当前位置的相邻的四个位置上的数字,所以我们在一开始的时候需要声明一个二维列表来记录递归的时候填入的数字,这样就可以使用这个列表来检查是否存在冲突,并且填完10个数字之后可以输出对应的值查看是否正确,我们可以声明一个3 * 4的二维列表,其中第一个位置与最后一个位置是没有用的,我们在判断是否存在冲突的时候检查左边/上边/左上角这三个位置的时候可以忽略掉这些位置

② 总的来说是在for循环中进行递归,尝试0-9的数字填入到当前(x,y)位置,检查当前填入的数字是否存在冲突,如果不存在冲突那么可以填入当前的数字,这道题目其实是典型的dfs应用的题目,都是比较常规的套路,答案是1580

③ 第二种思路是使用全排列的思想,我们可以先生成1-10的全排列,然后在递归的出口判断生成的全排列的对应位置是否存在冲突如果不存在冲突那么计数加1即可

3. 代码如下:

from typing import List


class Solution:
    count = 0
    # 只需要判断当前填入的数字的左边/上边/左上角/右上角是否相邻即可
    def check(self, n: int, x: int, y: int, res: List[List[int]], vis: List[int]):
        # 因为第一个数字res[0][0]是不需要进行检查的所以在判断的时候需要筛选掉: res对应位置不等于-1表示的是当前的位置不是第一个位置
        if y - 1 >= 0 and res[x][y - 1] != -1 and abs(n - res[x][y - 1]) == 1: return False
        if x - 1 >= 0 and res[x - 1][y] != -1 and abs(n - res[x - 1][y]) == 1: return False
        if x - 1 >= 0 and y - 1 >= 0 and res[x - 1][y - 1] != -1 and abs(n - res[x - 1][y - 1]) == 1: return False
        # 右上角的位置根本不可能为x = 0 y = 0的位置所以不用加上res对应位置是否等于-1的条件
        if x - 1 >= 0 and y + 1 < len(res[0]) and abs(n - res[x - 1][y + 1]) == 1: return False
        return True

    def dfs(self, x: int, y: int, res: List[List[int]], vis: List[List[int]]):
        if x == 2 and y == 2:
            for i in range(10):
                if vis[i]: continue
                # 检查最后一个数字
                if self.check(i, x, y, res, vis):
                    self.count += 1
                    res[x][y] = i
                    print("  ", end="")
                    print(res[0][1], res[0][2], res[0][3])
                    print(res[1][0], res[1][1], res[1][2], res[1][3])
                    print(res[2][0], res[2][1], res[2][2])
                    print()
                    return
            return
        for i in range(10):
            # 当前的数字之前已经使用过了
            if vis[i] == 1: continue
            if self.check(i, x, y, res, vis):
                vis[i] = 1
                res[x][y] = i
                # 二维方格中的递归经常使用下面的方式进行换行
                self.dfs(x + (y + 1) // 4, (y + 1) % 4, res, vis)
                # 回溯
                vis[i] = 0
                res[x][y] = -1

    def squareFill(self):
        # res用来记录递归过程中填入的数字
        res = [[-1] * 4 for i in range(3)]
        # print(res)
        # vis用来记录递归过程中使用过的数字
        vis = [0] * 10
        self.dfs(0, 1, res, vis)
        return self.count


if __name__ == '__main__':
    print(Solution().squareFill())

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值