八皇后问题
八皇后问题
众所周知,八皇后问题,就是给你一个8*8的棋盘,你有8个皇后棋子,如何摆放棋子,使得他们不互相攻击,也就是说,如何摆放,使得任意两个棋子不在同一行,不在同一列,也不在同一个对角线。已经知道,有92种摆法。
思路解析
这是一道经典的回溯问题,很容易想到,我们可以搜索每一个位置,如果这个位置合适了,就把棋子放上去,那么如何确定合适位置?可以一行一行看。
首先看第一行,这时我们要摆放第一个棋子,这个棋子是没有限制的,它可以放在第一行的任意一列,那我们就可以一个一个试,假设放在了第一列。当我们在第二行放第二个棋子的时候,我们就要考虑了,因为此时棋盘上已经有了一个棋子了,如果我们也放在第一列,显然不合适(此时两个棋子在同一列,不符合要求),

如果放在第二列,也不可以(两个棋子在同一对角线,也不符合要求),

如果放在第三列,这个时候,就没有问题了,这是个合适的位置。

以此类推,我们每放一个棋子,只要符合上面三个要求,这个位置就可以放(不需要考虑行了,因为我们本来就是一行放一个)。那么我们可以建几个数组col[],left[],right[],分别表示列,左对角,右对角,初始值为0,如果这列,或者这个左(右)对角已经有皇后了,就标为1,每准备放一个棋子,就在这几个数组里判断一下。
难点分析
其实col数组很好表示,棋子在i列放置,那就是col[I] = 1即可,对于left right可能要麻烦一点,通过找规律可以发现:

(这是一个8*8的表格,模拟棋盘,图中不同颜色代表不同的左对角线,共有15条左对角线)可以发现,每一个处在同一个左对角线的单元格的行坐标-列坐标的差值是固定的,图中数字表明了一些对角线的差值,有些差值为负数,为了符合c++数组下标的规范,我们把每个差值都加上7(最小差值为-7,这也是加上7的理由),这样就可以用下标来表示每一个左对角线。右对角线也有规律,同一个右对角线的单元格的行列坐标和相同,同样我们把每个和-2,为了满足数组下标。这样,我们就可以用这些数组来表示,列,对角线了。
完整代码
1 #include <stdio.h> 2 #include <iostream> 3 #include <cstring> 4 using namespace std; 5 int cnt = 0; //方便输出用的计数器 6 int a[9]; // 用一维数组来记录每一行的皇后排列情况,节省空间 (虽然我感觉也没节省多少) 7 int Left[15]; // 左对角线,记录某条对角线上是否有皇后 8 int Right[15]; // 右对角线 同上 9 int col[9]; // 列数组,记录某列是否有皇后 10 void eight(int line) 11 { 12 if(line == 9) // 注意,此处应该是9,不能是8,因为第8行也要有皇后 13 { 14 // 此处是打印结果,可以跳过 15 printf("----Case %d----\n",++cnt); 16 for(int i = 1; i <= 8; i++) 17 { 18 for(int j = 1; j <= 8; j++) 19 { 20 if(j == a[i]) printf(" @"); 21 else printf(" #"); 22 } 23 cout << endl; 24 } 25 cout << endl; 26 return; 27 } 28 if(line == 1) // 如果是第一行,皇后可以随便放,循环遍历每个位置 29 { 30 for(int i = 1; i <= 8; i++) 31 { 32 a[1] = i; // 为列数组,左数组,右数组做标记 33 col[i] = 1; 34 Left[8-i] = 1; 35 Right[i-1] = 1; 36 // 进行递归搜索 37 eight(line+1); 38 // 注意,回溯回来,一定要标记清零,数组归位 39 Left[8-i] = 0; 40 Right[i-1] = 0; 41 col[i] = 0; 42 } 43 } else { // 如果不是第一行,则遍历每一行,寻找合适位置 44 for(int i = 1; i <= 8; i++) 45 { // 如果该列,左对角线,右对角线,都没有皇后,则该位置可以放置皇后 46 if(col[i] == 0 && Left[line-i+7] == 0 && Right[line+i-2] == 0) 47 { 48 // 操作同上,依然注意,回溯回来,数组归位 49 a[line] = i; 50 col[i] = 1; 51 Left[line-i+7] = 1; 52 Right[line+i-2] = 1; 53 eight(line+1); 54 col[i] = 0; 55 Left[line-i+7] = 0; 56 Right[line+i-2] = 0; 57 } 58 } 59 } 60 } 61 int main() 62 { 63 // 四个memset可能是多此一举,有备无患吧 64 memset(a,0,sizeof(a)); 65 memset(col,0,sizeof(col)); 66 memset(Right,0,sizeof(Right)); 67 memset(Left,0,sizeof(Left)); 68 eight(1); // 从第一行开始搜索 69 return 0; 70 }

浙公网安备 33010602011771号