填字游戏-回溯法-Python

问题描述:

在3*3个方格的方阵中要填入数字1到N(N>=10)内的某9个数字,每个方格填一个整数,使得所有相邻两个方格内的两个整数之和为质数。试求出所有满足这个要求的各种数字填法。

Python实现:

# bl[i]来记录数字i是否使用过,
# di[i]用来记录下一个可以插在数字i后面的与其和为质数的数字在pr[i][]中的位置。
# 用pr[i][j]来存储按数字从小到大的顺序得出的与数字i和为质数的第j个数字,
# 例如:pr[1][2]存储的是与数字1的和为质数的第二个数字,我们可以通过查询
# 数组pr[][]的第一行找出第二个不为 0 值,然后将当前数组单元的列号存储到pr[1][2]中,即pr[1][2] = 4。
# 算法思想是通过查询二维数组pr[][],来确定下一个可以插入数组num[]的未使用过的数字,并记录该数字位于数组pr[][]的位置,
# 以便回溯时寻找下一个符合要求的数字。如果不存在这样的未使用的数字,则需要回溯到上一个已插入num[]的数字,
# 寻找下一个可以插在该数字后面的未使用过的数字进行插入,如果所有的数字都已经插入到num[]中,计数器 number++
# 进行回溯,重复上述操作,寻找其他符合要求的序列。
# 这里面有一个关键在于处理位于num数组右下角四个格子的数时,不光需要判断与前一个数i的关系,还需要判断其头顶上的元素 j 之和是否为素数。
# i,j 代表着,已经入栈的数字,在即将扩展的位置,a,b 的左边或者上边。

import math


def isPrime(n):  # 判断是否为质数
    t = int(math.sqrt(n)) + 1  # 开平方,上取整
    for k in range(2, t+1):
        if not(n % k): return False
    return True


def numberPlace(n):
    n = n + 1  # 包含第 n 个数字
    number = 0
    pr = [[0] * n for _ in range(n)]
    bl = [0] * n  # 判断 下标 i 是否已经使用过, 0 未使用过,1 使用过
    di = [1] * n  # 记录当前数值可结合的数值(用于回溯)相当于迷宫问题中的方向,1表示可使用,0表示不可使用
    num = [[0] * 3 for _ in range(3)]

    for i in range(1, n):  # pr 记录 i 和那些数字结合可以是一个质数
        k = 1
        for j in range(1, n):
            if i != j and int(math.fabs(i-j)) % 2 != 0:
                if isPrime(i+j):
                    pr[i][k] = j
                    k += 1

    for i in range(1, n):
        num[0][0], bl[i], k = i, 1, 1  # 第一个元素进入方格,数字 i, 已经使用过
        a, b = int(k / 3), k % 3  # 下一个位置
        while k:  # 表示栈指针
            while k < 9 and pr[i][di[i]]:  # 当前结点,还存在可以扩展的数字
                if not bl[pr[i][di[i]]]:  # 可扩展的数字没有被使用过
                    if a == 0 or b == 0 or isPrime(j+pr[i][di[i]]):
                        num[a][b] = pr[i][di[i]]  # 相当于进栈
                        bl[num[a][b]] = 1  # 已经使用过
                        di[i] += 1  # 相当于前一个数字,扩展到了下一个
                        if b == 2:  # 说明在最后一列上,那么下一个可扩展的数子,在同一行第一个数字的下面
                            i = num[a][0]
                        elif a != 0:  # num数组右下角四个格子的数
                            i, j = num[a][b], num[a-1][b+1]
                        else:
                            i = num[a][b]
                        k += 1
                        a, b = int(k / 3), k % 3  # 下一个位置
                    else: di[i] += 1
                else: di[i] += 1

            if k == 9:  # 9个数字都全部填好
                number += 1
            # -----  类似于 出栈 过程
            k -= 1
            a, b = int(k / 3), k % 3  # 获取行列 ,并恢复环境
            bl[num[a][b]], di[num[a][b]] = 0, 1
            if a == 0 and b == 0:  # 第一个数字
                i = num[0][0]
            elif a == 0:  # 第一行的数字
                i = num[a][b-1]
            elif b == 0:  # 第一列的数字
                i = num[a-1][0]
            else:  # num数组右下角四个格子的数
                i, j = num[a][b-1], num[a-1][b]
    return number


if __name__ == '__main__':
    print(numberPlace(20))

发现问题,请留言指教哦



你可能感兴趣的:(Python,算法)