N皇后问题完全优化指南 | aheadlead的生活博客


N皇后问题完全优化指南 | aheadlead的生活博客

第二个版本 更好的描述状态
我们可以直接用一个整数型的一维数组来描述一个棋盘:chessboard[i]的含义是第i列的皇后在chessboard[i]行上。


// 判断当前局面是否合法 int is_valid()
{
int queen1, queen2;
for (queen1=0; queen1<N; ++queen1)
{
for (queen2=0; queen2<queen1; ++queen2)
{
if (chessboard[queen1] == chessboard[queen2]) // 横向冲突
return false;
if (ABS(chessboard[queen1]-chessboard[queen2]) == ABS(queen1-queen2)) // 斜向冲突
return false;
}
}
return true;
}
// 把第queen_number号皇后摆到棋盘上去
void place_queen(int queen_number)
{
int row;
if (queen_number == N)
{
// 如果已经摆了N个皇后
// 就判断当前局面是否合法
if (is_valid())
solution_total = solution_total+1;
return;
}
// 枚举新皇后的位置
for (row=0; row<N; ++row)
{
// 将第queen_number号皇后摆到棋盘的row行column列
chessboard[queen_number] = row;
// 放置下一个皇后
place_queen(queen_number+1);
}
}
第三个版本 剪枝优化
如果你摆到第k个皇后时候(k{1,2,3,,N}),发现第k个皇后和第1个皇后到第k1之间的某一个皇后已经发生冲突了,那么第k号皇后后面的皇后也就没必要摆了。
这里的is_vaild()函数和之前的版本的含义已经有点不太一样了。在这里每放一个皇后就判断这个皇后是否符合互不攻击规则,而之前的版本,是所有的皇后都放好之后,才判断所有的皇后对,是否都符合互不攻击规则。这里is_vaild()的时间复杂度是线性的。

int is_vaild(int current_queen) {
int queen;
for (queen=0; queen<current_queen; ++queen)
if (chessboard[queen] == chessboard[current_queen] ||
ABS(chessboard[queen]-chessboard[current_queen]) == ABS(queen-current_queen))
return false;
return true;
}
// 把第queen_number号皇后摆到棋盘上去
void place_queen(int queen_number)
{
int row;
if (queen_number == N)
{
// 如果已经摆了N个皇后
solution_total = solution_total+1;
return;
}
// 枚举新皇后的位置
for (row=0; row<N; ++row)
{
// 将第queen_number号皇后摆到棋盘的row行queen_number列
chessboard[queen_number] = row;
if (is_vaild(queen_number)) // 判断当前局面下queen_number号皇后与之前的换后是否有冲突
{
place_queen(queen_number+1); // 放置下一个皇后
}
}
}
第四个版本 is_valid()的优化
我们有能力在放置某个皇后的时候,知道将要放置的这个皇后的行有没有其他的皇后。
我们再声明一个数组row_available[]用来记录每一行有没有被别的皇后占用,row_available[i]==1代表当前第i行存在皇后,若row_available[i]==0代表第i行不存在皇后。
这样我们可以在某次放置皇后时,直接的知道这一行有没有别的皇后。

void place_queen(int queen_number) {
int row;
if (queen_number == N)
{
// 如果已经摆了N个皇后
solution_total = solution_total+1;
return;
}
// 枚举新皇后的位置
for (row=0; row<N; ++row)
{
// 将第queen_number号皇后摆到棋盘的row行queen_number列
if (row_available[row] == 0)
{
row_available[row] = 1;
chessboard[queen_number] = row;
if (is_vaild(queen_number)) // 判断当前局面下queen_number号皇后与之前的换后是否有冲突
{
place_queen(queen_number+1); // 放置下一个皇后
}
row_available[row] = 0;
}
}
}
第五个版本 去掉is_valid()
既然我们能用一个数组描述所有的行是否已经存在皇后了,那么对于对角线我们能不能做一样的优化呢?看到这种句式,马上就能反应过来”是可以的“的答案...
没错,上两张图。
QQ20141106-5@2x
QQ20141106-6@2x我们先看第二张图(因为第二张图离这行文字比较近,充分的体现了我人性化的关怀 ; ) )。可以看到,格子里的数字是行号减去列号加上边长减一。
如右上角的格子,0=07+81
可以看到,每个数字对应一条右下方向(↘)的对角线,我们声明一个rdiag_available[]用于记录这条对角线上有没有皇后即可。处理的方法和上一版本一样。
左下方向(↙,第一张图)的可以类似地声明一个数组ldiag_available[]来记录。
diag代表diagonal,对角的意思。

void place_queen(int queen_number) {
int row;
if (queen_number == N)
{
// 如果已经摆了N个皇后
solution_total = solution_total+1;
return;
}
// 枚举新皇后的位置
for (row=0; row<N; ++row)
{
if (row_available[row] == 0 &&
rdiag_available[row-queen_number+N-1] == 0 && // 右下对角线方向
ldiag_available[row+queen_number] == 0) // 左下对角线方向
{
row_available[row] = 1;
rdiag_available[row-queen_number+N-1] = 1;
ldiag_available[row+queen_number] = 1;
chessboard[queen_number] = row; // 将第queen_number号皇后摆到棋盘的row行queen_number列
place_queen(queen_number+1); // 放置下一个皇后
row_available[row] = 0;
rdiag_available[row-queen_number+N-1] = 0;
ldiag_available[row+queen_number] = 0;
}
}
}
第六个版本 镜像优化
不难观察到,对于某个局面,我们对它进行上下翻转之后,得到的新局面,仍然是一个符合互不攻击规则的局面。根据这一点,我们可以将列举局面的范围缩小一半(对于偶数边长的棋盘来说是一半,对于奇数边长的棋盘却不是)
第一个皇后(最左边的皇后)我们可以考虑放置在第0行到第7行,但现在只需要考虑放置在第0行到第3行了,因为当第一个皇后在第3行到第7行的时候的局面,总是可以通过上下翻转,由第一个皇后在第0行到第3行的某个局面得来。
也就是说第一个皇后在第0行到第3行时找到了一个答案,我们直接上下翻转就能得到另外一个答案。(易证符合互不攻击规则的局面进行上下翻转前后,不可能是同一个局面。)
所以说,我们第一个皇后只需要枚举一半的范围。
对于偶数边长,我们只用列举一半,每种符合规则的情形计数两次即可。但对于奇数边长,我们要特殊处理一下中间的那一行,中间的那一行不存在上下对称之说,所以中间这一行只要计数一次。

// 皇后从start_row行开始放,直到end_row行(前闭后开) void place_queen(int queen_number, int start_row, int end_row)
{
int row;
if (queen_number == N)
{
// 如果已经摆了N个皇后
solution_total = solution_total+1;
return;
}
// 枚举新皇后的位置
for (row=start_row; row<end_row; ++row)
{
if (row_available[row] == 0 &&
rdiag_available[row-queen_number+N-1] == 0 && // 右下对角线方向
ldiag_available[row+queen_number] == 0) // 左下对角线方向
{
row_available[row] = 1;
rdiag_available[row-queen_number+N-1] = 1;
ldiag_available[row+queen_number] = 1;
chessboard[queen_number] = row; // 将第queen_number号皇后摆到棋盘的row行queen_number列
place_queen(queen_number+1, 0, N); // 放置下一个皇后
row_available[row] = 0;
rdiag_available[row-queen_number+N-1] = 0;
ldiag_available[row+queen_number] = 0;
}
}
}

// 开始放置皇后
place_queen(0, 0, N/2);
// 由于上下镜像导致对称的关系,解的数量能直接乘以二
solution_total = solution_total*2;
// 当棋盘边长为奇数的时候要特别考虑一下第一个皇后放N/2行的情况(不要忘了是从第0行开始数)
if (N % 2 == 1)
place_queen(0, N/2, N/2+1);
printf("%d\n", solution_total);


第七个版本 位运算 http://www.matrix67.com/blog/archives/266

int upperlim = (1<<N)-1; void test(int row, int ld, int rd)
{
int pos, p;
if (row != upperlim)
{
pos = upperlim & (~(row | ld | rd));
while (pos)
{
p = pos & (~pos + 1);
pos -= p;
test(row | p, (ld | p) << 1, (rd | p) >> 1);
}
}
else
solution_total++;
}
第八个版本 位运算上的镜像优化

for (i=0; i<N/2; ++i) test(1<<i, 1<<(i+1), 1<<(i-1));
solution_total = solution_total*2;
if (N & 1)
test(1<<(N/2), 1<<(N/2+1), 1<<(N/2-1));
第九个版本 递归改非递归

第十个版本 发挥多核优势
如果你只要得到N皇后(N要很大)问题的一个解,可以试试通过matrix67大牛介绍的神奇环中环构造法(传送门)。也可以试试一种所谓人工智能里面的“乱搞”算法,大题思路就是先随机生成一个局面,然后不断的选择一个最佳的皇后并调整她的位置,使得整体朝着没有冲突的方向发展。出于自身蒟蒻的能力,我没法严谨的描述这个算法,感兴趣的同学可以去看看(传送门)。
如果你只想得到N皇后下,解的数量有多少的话,其实存在一种方法能在常数时间内算出来...(众人:那你写这篇文章有P用啊...)
最后推荐一篇文章,题目叫《N皇后问题解的构造及等价性分析》,囊括了我对N皇后问题的认知。
Related: 八皇后问题算什么,来看看无穷皇后问题吧
皇后也疯狂:如何在1分钟内摆放300万个皇后
Read full article from N皇后问题完全优化指南 | aheadlead的生活博客

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts