python算法题——数独 p2676

文章介绍了如何使用深度优先搜索(DFS)和回溯算法来解决数独问题。关键在于编写is_valid函数检查数字是否符合数独规则,以及backtrack函数进行递归搜索和回溯。在回溯过程中,当找到一个可行解时返回True,否则在所有可能的数字尝试后回溯并清空当前单元格。最后,将二维列表转换为数独字符串输出。
摘要由CSDN通过智能技术生成

数独

描述

数独是一项非常简单的任务。如图所示,将 9 行 9 列的方桌分成 9 个 3x3 的小正方形。在某些单元格中,写入从 1 到 9 的十进制数字。其他单元格为空。目标是用从 1 到 9 的十进制数字填充空单元格,每个单元格一位数字,这样在每一行、每列和每个标记的 3x3 子方块中,出现从 1 到 9 的所有数字。编写一个程序来解决给定的数独任务。

输入

输入数据将从测试用例的数量开始。对于每个测试用例,后面有 9 行,对应于表的行。在每一行上给出一个正好由 9 个十进制数字组成的字符串,对应于此行中的单元格。如果单元格为空,则用 0 表示。

输出

对于每个测试用例,程序应以与输入数据相同的格式打印解决方案。必须根据规则填充空单元格。如果解决方案不是唯一的,则程序可以打印其中任何一个。

示例输入

1
103000509
002109400
000704000
300502006
060000050
700803004
000401000
009205800
804000107

示例输出

143628579
572139468
986754231
391542786
468917352
725863914
237481695
619275843
854396127

数独,用DFS,其中最需要注意的便是剪枝

1.一个9宫格内不能有相同的数字

2.一行不能有相同的数字

3.一列不能有相同的数字

(有一种暴力的思路,就是对每一个0的地方判断是否只有一种情况,如果只有一种情况,就填入那个数字,这样的时间复杂度应该是9 * 9 * 9 ** 2)

回溯算法

def solve_sudoku(board):
    """
    使用回溯算法解决数独问题
    """
    def is_valid(row, col, num):
        """
        检查填入的数字是否符合数独规则
        """
        for i in range(9):
            # 检查该行是否有相同数字
            if board[row][i] == num:
                return False
            # 检查该列是否有相同数字
            if board[i][col] == num:
                return False
            # 检查该九宫格是否有相同数字
            if board[3*(row//3)+i//3][3*(col//3)+i%3] == num:
                return False
        return True
    
    def backtrack(row, col):
        """
        回溯算法主函数
        """
        # 搜索到最后一行的下一行,说明已找到一个可行解
        if row == 9:
            return True
        # 搜索当前行的下一列
        next_row = row if col < 8 else row+1 #妙啊!!!如果列读完就换行
        next_col = (col+1) % 9
        # 如果当前位置已经填入数字,则跳过
        if board[row][col] != '0':
            return backtrack(next_row, next_col)
        # 尝试填入数字
        for num in range(1, 10):
            if is_valid(row, col, str(num)):
                board[row][col] = str(num)
                if backtrack(next_row, next_col):
                    return True
                board[row][col] = '0'
        # 无解,回溯
        return False
    
    # 将输入的数独字符串转换为二维列表
    board = [list(row) for row in board]
    # 调用回溯算法
    backtrack(0, 0)
    # 将二维列表转换为数独字符串并返回
    return [''.join(row) for row in board]
​

这个代码还是有很多值得学习的地方的:

1.写一个is_valid函数,然后传入参数为row, col, num 直接去比较每一行每一列则是使用board[i][col]还有board[row][i]来遍历,还有九宫格里面的变换,board[3*(row//3)+i//3][3*(col//3)+i%3]来遍历,3*(row//3)就是行的开始,然后0~9每3个row的地方加上一i//3,而col的地方则是3个一循环i%3,也是3*(col//3)从头开始,这很巧妙。只要发现有雷同的就返回False

2.backtrack函数中传入两个参数,一个row,一个col,妙的地方在于对next_rownext_col的处理

next_row = row if col < 8 else row+1 next_col = (col+1) % 9

next_row里用了赋值里面嵌套if,如果列的地方大于9则说明要换行了(因为索引从0开始所以这里是小于8),if col < 8 next_row = row, else row + 1

next_col直接模9

3.if board[row][col] != '0': return backtrack(next_row, next_col) # 如果当前位置已经填入数字,则跳过

这里跳过不仅仅是continue,而是直接调用了backtrack,在return里面调用函数!

4.if backtrack(next_row, next_col): return True

这里需要注意什么情况下会返回True给他,

if row == 9: return True

我们看到只有这里,那么当整个代码结束后就会一路True下去

5.当然如果num里面都不符合,那么return False, 即回溯,在

if backtrack(next_row, next_col): return True这下方加上board[row][col] = '0'还原状态

6.return [''.join(row) for row in board]这个是什么操作呢?

return [''.join(row) for row in board]是将数独的解从二维列表转换为字符串的操作。

solve_sudoku()函数中,我们将输入的数独字符串转换为一个由列表表示的二维棋盘,其中每个空格用'.'表示。在回溯算法求解完毕后,我们需要将二维棋盘转换为数独字符串,以便输出答案。

[''.join(row) for row in board]是一个列表推导式,它遍历二维列表board中的每一行,将该行中的字符拼接成一个字符串,并将该字符串添加到一个新的列表中。最终,该列表中的每个元素都是一个由字符串表示的数独行,将它们连接起来即可得到完整的数独字符串。

完整代码

def solve_sudoku(board):
    """
    使用回溯算法解决数独问题
    """
​
    def is_valid(row, col, num):
        """
        检查填入的数字是否符合数独规则
        """
        for i in range(9):
            # 检查该行是否有相同数字
            if board[row][i] == num:
                return False
            # 检查该列是否有相同数字
            if board[i][col] == num:
                return False
            # 检查该九宫格是否有相同数字
            if board[3 * (row // 3) + i // 3][3 * (col // 3) + i % 3] == num:
                return False
        return True
​
    def backtrack(row, col):
        """
        回溯算法主函数
        """
        # 搜索到最后一行的下一行,说明已找到一个可行解
        if row == 9:
            return True
        # 搜索当前行的下一列
        next_row = row if col < 8 else row + 1  # 妙啊!!!如果列读完就换行
        next_col = (col + 1) % 9
        # 如果当前位置已经填入数字,则跳过
        if board[row][col] != '0':
            return backtrack(next_row, next_col)
        # 尝试填入数字
        for num in range(1, 10):
            if is_valid(row, col, str(num)):
                board[row][col] = str(num)
                if backtrack(next_row, next_col):
                    return True
                board[row][col] = '0'
        # 无解,回溯
        return False
​
    board = [list(row) for row in board]
    # 调用回溯算法
    backtrack(0, 0)
    # 将二维列表转换为数独字符串并返回
    for i in range(9):
        for j in range(9):
            print(board[i][j], end = "")
        print("")
    return [''.join(row) for row in board]
​
if __name__ == '__main__':
    # 将输入的数独字符串转换为二维列表
    board = [[] for i in range(9)]
    for i in range(9):
        board[i] = input()
    solve_sudoku(board)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值