Python实现蓝桥杯2n皇后问题

蓝桥杯2n皇后问题

问题

问题描述
  给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。
输入格式
  输入的第一行为一个整数n,表示棋盘的大小。
  接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为0,表示对应的位置不可以放皇后。
输出格式
  输出一个整数,表示总共有多少种放法。
样例输入
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
2
样例输入
4
1 0 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
0

分析

首先们要知道八皇后问题是怎么解决的。我们通过递归回溯的方法,利用生成器为棋盘上的每一个棋子进行排序,然后将合法的数据记录下来。其中的yield函数可能不好理解,我们在下面介绍它

yield

这个就是生成器,有这个的函数,产生的结果将会作为可迭代对象调用。当函数遇到yield之后会停止运行,注意,函数只是停止了而没有结束,这个和return是不一样的。yield前面的功能和参数依然可以调用,参与计算。

我们来看下面的例子:

def generate():
  yield 1
  yield 2

这个函数我们可以把它抽象成[1,2]没错,他就是通过yield变成了可迭代对象,但它不同于列表,我们在这里只是想让读者更好地明白这个yield到底在干什么。

现在我们先不说2n皇后,我们把重心先放在八皇后。

八皇后问题python解决

def conflict(queen_lis, new_queen):
    """判断棋子是否符合规则"""
    for index, queen in enumerate(queen_tup):
        if abs(new_queen - queen) in (len(queen_tup) - index, 0):  # 判断表达式:垂直距离和水平距离
            # 列数之差是否等于行数之差,或者列数之差为零
            # 注意,len(queen_tup)是行数。因为第一行不进入循环,所以,这里的行数要比len出来的值加一,也就是,len(queen_tup)如果等于3,那么对应的是第四行
            # 对应的,index也是要加一的。
            return False
    return True


def arrange_queen(num, queen_lis=[]):
    """
    :param num:棋盘的的行数,当然数值也等于棋盘的列数
    :param queen_tup: 设置一个空队列,用于保存符合规则的棋子的信息
    """
    for new_queen in range(num):  # 遍历一行棋子的每一列

        if conflict(queen_tup, new_queen):  # 判断是否冲突

            if len(queen_tup) == num - 1:  # 判断是否是最后一行
                yield [new_queen]  # yield关键字,返回当前的位置。

            else:
                # 若果不是最后一行,递归函数接着放置棋子

                for result in arrange_queen(num, queen_lis + [new_queen]):# 结果的迭代
                    yield [new_queen] + result # 这个是每个函数最终的地方。


for i in arrange_queen(4):
    print(i)

读者可以看代码旁的注释进一步的理解,我在这再解释一下这个递归回溯到底是怎么最终产生结果的:

​ 当函数的conflict函数返回True之后,我们会判断这个是不是在排棋盘中的最后一行,如果是的话,就说明前面三行已经排好,并且,最后一行也是合法的,这个时候,最后一次递归结束,并将结果返回给上一层递归,上一层递归收到返回值之后把它作为了可迭代对象传给result,然后此时的new_queen就是上一层函数的参数,result就是最后一层递归返回的结果。相加后又yield给了上上层递归。这个就是回溯的过程。

读者可能会疑惑,棋盘明明是二维数组,为什么这里产生的结果都是一维的,这里我做解释:

​ 我们这行数是通过递归来判断的,也就是说我们通过递归的次数来代替了行数,每递归一次行数就加一,同时通过返回queen_lis的长度来判断当前到了第几行,因为queen_lis里面加一个参数,就说明这个参数是合法的,此时参数保留,只要不是最后一层,那么就会进入下一次递归,同时这个参数就被加到了queen_lis里面。所以这个列表的长度就是我们要的行数。这个行数的判断极其的重要,因为之后另一个皇后的判断,我们又要利用到当前的行数。

上面是以四乘四的棋盘为例子举例的,了解以上的方法后,我们这个八皇后问题就解决了。

现在我们来探讨2n皇后

2n皇后解决方案和解释

代码

nums = eval(input())
n = 0
list_empty = []
while n < nums:
    hang = [int(i) for i in input().split()]
    list_empty.append(hang)
    n +=1
def conflict(queen_list,new_queen,black=None):
    num = len(queen_list)
    try:
        col = black[num]
    except:
        pass
    try:
        if len(black) != 0:
            if new_queen == col:
                return True
    except:
        pass
    if list_empty[num][new_queen] == 0:
        return True
    for index,queen in enumerate(queen_list):
        if abs(new_queen-queen) in (0,num-index):
            return True

    return False


def queen(num, queen_list=[]):
    for new_queen in range(num):
        if not conflict(queen_list, new_queen):
            if len(queen_list) == num -1:
                yield [new_queen]

            else:
                for result in queen(num,queen_list+[new_queen]):
                    yield [new_queen]+result

black_queen = list(queen(nums))
def queen_white(num,black,queen_list=[]):
    for new_queen in range(num):
        if not conflict(queen_list,new_queen,black):
            if len(queen_list) == num -1:
                yield [new_queen]

            else:
                for result in queen_white(num,black,queen_list+[new_queen]):
                    yield [new_queen]+result
end_num = []
sum = 0
for black in black_queen:
    white_queens = list(queen_white(nums,black))
    if len(white_queens)==0:
        continue
    end_num.append(len(white_queens))


for i in end_num:
    sum +=i

print(sum)

​ 现在,我们先排黑皇后再排白皇后。此时,我们需要两个排列皇后的函数,但是判断冲突的条件也要改一下,来适应和白皇后不同的冲突需求。

​ 我们先排好黑皇后,将黑皇后的位置信息保存在black_queen里面,接下来,我们开始排白皇后的位置,注意,白皇后位置的总和,就是题目的答案,这个应该很好理解,因为是在黑皇后位置的基础上,如此。

​ 在上面的代码中,我们在conflict函数里面加了一个black,这个就是黑皇后的位置参数。我们通过在冲突判断函数中传入black参数,从而可以判断当前白皇后的位置是否和黑皇后的位置冲突。

​ 同时,我们加了个棋盘是否为0的判断,用来判断当前位置是否合法。

queen_white中我也传入了black参数,就是为了不用每次都定义一遍冲突函数,直接在调用排序的时候就可以自动传入参数。

​ 接下来,就是通过for循环,将黑皇后的位置不停地传入判断黑皇后的位置函数里面,排序白皇后。

​ 最后,将排序下来的次数存入到空列表中,最后把它们相加,最终打印出来。

​ 我们发现,这2n皇后的解决,是基于八皇后的思想基础上的,代码上也就加了一个黑皇后位置参数判断,和棋盘合法性判断,其它并没有什么实质性的改变。

​ 读者可能注意到了里面有异常处理,原因是因为本来应该是定义两个冲突函数的,但是这里我把他们合并了,变成了一个,所以,black一开始是空的,所以加了个异常处理。

你可能感兴趣的:(计算机,算法,蓝桥杯)