藍橋杯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一開始是空的,所以加了個異常處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章