1. 程式人生 > 其它 >搜尋演算法入門之N皇后問題

搜尋演算法入門之N皇后問題

技術標籤:演算法演算法c++

搜尋演算法入門之N皇后問題

題目描述

在國際象棋中,皇后可以攻擊與他在同一條水平線,垂直線,對角線的棋子,現在有一張 N*N 的棋盤,在上面放置 N 個皇后。有多少種使皇后不能互相攻擊的方案?

輸入格式

輸入一個N(N<=15),表示有一個N*N的棋盤.

輸出格式

輸出只有一行,輸出共有多少種可以擺放皇后的方式

樣例輸入

8

樣例輸出

92

解題思路

逐行(列)搜尋。每測試一個位置,就檢查它是否與其他皇后衝突,如果衝突,回溯。
每放置一個皇后,就要記錄所在列,“\”對角線, “/”對角線都不能放置皇后了。

AC 程式碼

深度優先搜尋(DFS)

小白記錄成長,歡迎各路大佬前來教學
#include <iostream>
#include <string>
#include <algorithm>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int n;//n表示這是一個N*N的棋盤
int sum=0;//sum用於計數,記錄可以有多少种放置皇后的方法
bool column[20],cross1[50],cross2[50];
//column用於標記列,cross1用於標記主對角線,cross2用於標記副對角線 int pos[20];//pos是放置棋子的位置 void dfs(int row) { //當row與n相同時,說明已經放置到了最後一行,此時計數器++,並且回溯返回上一層 if(row==n) { sum++; return ; } for(int i=0; i<n; i++) { if(!(column[i] || cross1[row-i+n] || cross2[row+i]))//判斷是否可以放置皇后 {
//對皇后已經控制的行和列進行標記 column[i]=cross1[row-i+n]=cross2[row+i]=true; pos[row]=i; dfs(row+1); //解除標記,這樣才能在新位置上放置皇后 column[i]=cross1[row-i+n]=cross2[row+i]=false; } } } int main() { cin>>n; //首先將列,主對角線,副對角線的標記全部標位false,表示起始狀態下所有位置都沒有放置皇后 memset(column,false,sizeof(column)); memset(cross1,false,sizeof(cross1)); memset(cross2,false,sizeof(cross2)); dfs(0); cout<<sum<<endl; return 0; }

狀態壓縮

把狀態放在一個4位元組的int整型變數中,並且使用位運算————這樣,速度會比普通演算法的快很多

#include <iostream>
#include <string>
#include <algorithm>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

//sum用來記錄皇后放置成功的不同佈局數;upperlim用來標記所有列都已經放置好了皇后
int n, sum=0, upperlim=1;
void test(long row ,long ld,long rd)
{
    if(row != upperlim)
    {
        /*
            row,ld,rd進行“或”運算,求得所有可以放置皇后的列,對應位位0.
            然後再取反後“與”上全1的數,來求得當前所有可以放置皇后的位置,對應列改為1.
            也就是求取當前哪些列可以放置皇后。
        */
        long pos=upperlim & ~(row | ld | rd);
        while(pos)// 0 -- 皇后沒有位置可放,直接回溯
        {
            /*
                拷貝 pos 最右邊的 1 為 bit ,其餘 bit 置為 0
                也就是取得可以放皇后的最右邊的列
            */
            long p = pos & -pos;
            /*
                將 pos 最右邊的 1 的 bit 清零。
                也就是為獲取下一次的最右可用列使用作準備
                程式將來會回溯到這個位置繼續試探。
            */
            pos -= p;
            /*
                row + p , 將當前列置為 1 ,表示記錄這次皇后放置的列
                (ld + p) << 1, 表示當前皇后左邊相鄰的列不允許下一個皇后放置
                (ld + p) >> 1, 表示當前皇后右邊相鄰的列不允許下一個皇后放置
            */
            test(row + p, ( ld + p ) << 1 ,( ld + p )>>1 );
            /*
                此處的位移操作實際上是記錄對角線上的限制,只是因為問題都歸化到
                一行網格上來解決,所以表示為列的限制就可以了。顯然。隨著移位在
                每次選擇列之前進行,原來 N * N 網格中某個已放置的皇后鎮對其對角
                線上產生的限制都被記錄了下來
            */
        }
    }
    else
        sum++; // row 的所有位都為1,即找到了一個成功的佈局,回溯。
}
int main()
{
    cin>>n;
    // n 皇后只需要 N 位儲存,N列中某列有皇后則對應的 bit 置為 1
    upperlim = (upperlim << n+1) - 1;
    test(0,0,0);
    cout<<sum<<endl;
    return 0;
}