回溯算法筆記,並解決N皇后問題(Python實現)

在這裏插入圖片描述

1.概念

回溯算法實際上一個類似枚舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就“回溯”返回,嘗試別的路徑。
回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術爲回溯法,而滿足回溯條件的某個狀態的點稱爲“回溯點”。
許多複雜的,規模較大的問題都可以使用回溯法,有“通用解題方法”的美稱。

2.基本思想

在包含問題的所有解的解空間樹中,按照深度優先搜索的策略,從根結點出發深度探索解空間樹。當探索到某一結點時,要先判斷該結點是否包含問題的解,如果包含,就從該結點出發繼續探索下去,如果該結點不包含問題的解,則逐層向其祖先結點回溯。(其實回溯法就是對隱式圖的深度優先搜索算法)。
若用回溯法求問題的所有解時,要回溯到根,且根結點的所有可行的子樹都要已被搜索遍才結束。
而若使用回溯法求任一個解時,只要搜索到問題的一個解就可以結束。

白話:回溯法可以理解爲通過選擇不同的岔路口尋找目的地,一個岔路口一個岔路口的去嘗試找到目的地。如果走錯了路,繼續返回來找到岔路口的另一條路,直到找到目的地。

3.用回溯法解題的一般步驟:

1)針對所給問題,確定問題的解空間:首先應明確定義問題的解空間,問題的解空間應至少包含問題的一個(最優)解。
2)確定結點的擴展搜索規則
3)以深度優先方式搜索解空間,並在搜索過程中用剪枝函數避免無效搜索。


4. 實例:解決N皇后問題

  • 問題: 在n*n格的棋盤撒上放置彼此不受攻擊的n個皇后。按照國際象棋的規矩,皇后可以攻擊與之處在同一行或者同一列或者同一斜線上的棋子。N皇后問題等價於在n * n格的棋盤上放置n個皇后,任何2個皇后不放在同一行同一列同一斜線上。

  • 分析: 可以將nn的棋盤看成一個nn的表格,放置皇后Q,且需要滿足兩個條件

    • 條件1:同行同列不能放置兩個或大於兩個皇后
    • 條件2:皇后的斜線上不能存在皇后
  • 解題思路及步驟:

逆推導:行值是列表的第幾個元素,列值是列表元素的值
那麼,可以知道,當列表中出現兩個或者兩個以上的相同元素時,即不滿足條件1
爲了提高算法效率,我們可以將不考慮限制條件和考慮條件1相結合,那麼就是全排列算法

 1  2  3 4
1♛ ☐ ☐ ☐
2☐ ♛ ☐ ☐
3☐ ☐ ♛ ☐
4☐ ☐ ☐ ♛

從以上模型可得,
i,j表示行值,a[i],a[j]表示列值
|a[i]-a[j]| = |i-j|,即不滿足條件2
|a[i]-a[j]| != |i-j|,即滿足條件2
滿足條件2,可以將滿足條件1問題的所有解空間,進行條件限制,可以通過函數實現,這個函數即是剪枝函數
編程步驟:
首先定義一個列表,爲素材列表[1,2,3,…,n]
定義一個列表,存儲全排列的結果;對素材列表進行全排列,並存入該列表中,爲全排列列表
定義一個列表,用於存儲滿足條件2的列表,爲結果列表
根據結果列表,進行格式打印

代碼實現

#全排列函數
per_result = []
def per(lst,s,e):
    if s == e:
        per_result.append(list(lst))
    else:
        for i in range(s,e):
            lst[i],lst[s] = lst[s],lst[i]#試探
            per(lst,s+1,e)#遞歸
            lst[i],lst[s] = lst[s],lst[i]#回溯
#剪枝函數
def shear(lst):
    result = 0
    for i in range(len(lst)):
        for j in range(i+1,len(lst)):
            if(abs(lst[j] - lst[i]) == abs(j-i)):
                result += 1
    if(result > 0):
        return True
    else:
        return False
#格式打印函數
def stamp(st):
    for i in st:
        for j in range(len(i)):
            a = ("☐"*(i[j]-1)+"♛"+"☐"*(len(i)-i[j]))
            print(a,"\t","第{}個皇后放在棋盤的第{}列".format(j+1,i[j]))
        print(" ")#負責空行
def main():
    num = eval(input("請輸入皇后的個數:"))
    lst = [i+1 for i in range(num)]
    per(lst,0,num)
    queen_lst = []
    for i in per_result:
        if(shear(i) == False):
            queen_lst.append(i)
    stamp(queen_lst)
    print("共{:d}種可能".format(len(queen_lst)))  
if __name__=='__main__':
    main()

輸出樣例:
4皇后:
在這裏插入圖片描述
8皇后:
在這裏插入圖片描述


參考文章:
https://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html
https://blog.csdn.net/zhj_1121/article/details/103059144

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