觀察棋盤,要求皇后之間不能處在同行同列同一條斜線,求使得每行都有乙個皇后的放置方法共有多少種。
每嘗試放置乙個皇后,都可以把該位置所在的行、列標號用乙個陣列標記,含義表示該行該列已經被占用,同時所在斜列也要進行標記。所在斜線有左斜線和右斜線兩種,畫個**釋一下。
舉個栗子,對於(2,0)這個點,他所在左斜線為\(2-0 + n = 6\) (n=4),所在右斜線為\(2+0 = 2\) ,那麼在掃瞄第3行時,就避免嘗試在這兩條斜線上的點
標記原理清除之後,下一步就是搜尋框架了
因為每行或者每列只會放乙個皇后,所以我們用行號來作為乙個搜尋深度,每次搜尋當前行的乙個可行位置,對該位置所需要標記的都標記之後,進行下一行的搜尋。
細節請見**
#include using namespace std;
int n;
int d[20],cols[20],dia[100],dia2[100];
int res;
void dfs(int x)
for(int i=1;i<=n;i++)
}}int main()
- 1 = 1048575\) 對於求解我們所需的n皇后夠用了。
所以用乙個數字 \(a\) 表示所有列的占用情況(二進位制位為1則表示占用)
用b來表示右斜線的占用情況
用c來表示左斜線的占用情況
通過運算 \(a|b|c\) 就得到了當前行不可以填的所有情況
\(((1就得到了當前行可以填的位置集合(二進位制為1表示可以填)
如果對細節不清楚沒關係,後面會進一步講解
得到了這個集合又如何?難道要從低位到高位乙個乙個取嗎?
那這樣的話,複雜度不是等同於掃瞄陣列?
我們有更巧的辦法,每次可以直接\(o(1)\) 獲取二進位制表示中的最右邊的1的權值(權值就是那個權值,比如第2位上的1的權值就是\(2^2\))
\(lowbit(x) = x\& - x\)
位運算優先順序低,所以這裡先對減號進行運算
-x使得正數x求反+1,
舉個例子,對於18這個數字
18 = 0001 0010
~ => 1110 1101
+1 => 1110 1110
加一操作使得從低位的那些連續的1(從原碼中的0求反而來)全部懟到了最低位的那個0上面,高位都不變
再舉個大一點的例子
0000 1010 1000
~之後 1111 0101 0111
+1 1111 0101 1000
然後跟原來的數字做與運算,就得到了最左邊的1...
這樣就可以直接列舉可行的位置了。
接下來講一下如何對斜線進行標記
我們列舉了(0,1)這個位置,列標記為 0010,左斜線標記為0010,右斜線標記為0010 (別急,看下一步)
到第二行後,可以發現在右斜線上,我們是不能在(1,0)這個位置放置皇后的,也就是狀態應該為 0001 , 這個數字可以由 0010 右移直接得來(仔細想一下這個右移操作的含義,它使得斜線所標識的列號直接轉移)。對於左斜線也是同理。
所以我們可以通過左移|右移的操作來轉移斜線的狀態了。
#include using namespace std;
const int n = 1010;
int n;
long long res;
void dfs(int x, int a,int b, int c)//當前行x的狀態,a表示列狀態,b表示右斜線,c表示左斜線
int s=((1即找最左邊的1
dfs(x+1,a+z,(z+b)<<1,(c+z)>>1);//l,x,y能更新放的位置(對角線到下一行會移一列)右斜線左移,左斜線右移
s-=z;
}}int main()
這個優化會使得搜尋速率快78倍,效果還是很顯著的 n皇后 位運算版
n皇后問題是大家在遞迴裡會碰到的乙個經典問題。以前高中我學dfs的時候,老師首先讓我看的就是八皇后。不過這皇后的時間複雜度大家可想而知了。而接下來的位運算將這個效率重新提到乙個高度。我是以前在matrix67大牛那裡學的,最近資料結構實驗剛好碰到n皇后,就在這裡 複述 一遍吧。code void d...
位運算解決N 皇后問題
描述 位運算是定義在整數上的運算。但在做位運算的時候,並不把整數看作整數,而是將它們看做一系列二進位制數字,逐位進行運算。位運算有6種,他們的名稱,運算子及運算規則如下 與 and 5 6 4 101 110 100 或 or 5 6 7 101 110 111 異或 xor 5 6 3 101 1...
位運算n皇后 洛谷1219
老鐵已經很久沒有更博了。自從考完noip後老鐵一蹶不振,從此走上心理陰影無限大,吾將上下而求索的道路。又去石家莊聽課 並不能聽懂 於是自己學一學一些奇怪的演算法。題目描述 檢查乙個如下的6 x 6的跳棋棋盤,有六個棋子被放置在棋盤上,使得每行 每列有且只有乙個,每條對角線 包括兩條主對角線的所有平行...