八皇后问题就是在一个8*8的棋盘上任何两列的行、列、对角线上都不允许有其他元素,问在棋盘上放8个棋子共有多少种放法?
题目不难,想必很多人都有解决思路。但是你的解决思路是最简的吗?
假如第一行放一个那么第二行有7种放法,然后第3行又有6种放法,以此类推,共有8!=40320个。
递归都会写,关键是判断条件,如何简化判断。
int tot = 0; int c[100];
void search(int cur) {
if (cur == n) {
tot++; //累积多少种放法
for (int i = 0; i < n; i++)cout << c[i] << " ";//输出可行的排列
cout << endl;
}
else for (int i = 0; i < n; i++) {
int ok = 1;
c[cur] = i;//将i列赋给cur行
for (int j = 0; j < cur; j++) {
if (c[cur] == c[j] || cur - c[cur] == j - c[j] || cur + c[cur] == j + c[j]) {//判断是否有对角线上存在或者行上存在
ok = 0; break;
}
}
if (ok)search(cur + 1);//递归下一行
}
}
第12行是判断是否可行语句,本来我们的判断可能就是建立一个二维数组找出这个点所在的列是否有其他元素和它两个对角线上是否有其他元素(方法就是(x--,y--)每次循环判断是否用过,直到到达边界)这样一来又是三个for循环,提高了复杂度。原书作者采用一维数组存放位置信息。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
-1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
-2 | -1 | 0 | 1 | 2 | 3 | 4 | 5 |
-3 | -2 | -1 | 0 | 1 | 2 | 3 | 4 |
-4 | -3 | -2 | -1 | 0 | 1 | 2 | 3 |
-5 | -4 | -3 | -2 | -1 | 0 | 1 | 2 |
-6 | -5 | -4 | -3 | -2 | -1 | 0 | 1 |
-7 | -6 | -5 | -4 | -3 | -2 | -1 | 0 |
其中c[cur]==c[j]就是搜索前面的行有没有用过这一列(cur表示行,c[cur]表示这一行的列元素);cur-c[cur]就是主对角线上的值相等。所以只需要判断cur-c[cur]==j-c[j](j-c[j]表示前面的元素如果c[j]用过的话,那么它所在的对角线就符合上面的主对角线上的值。)同理,副对角线只需要将j+c[j]即可,同样判断是否符合条件。
改进版:
其中递归已经是最简了,那么我们想是否可以简化判断呢?
将判断的for循环去掉,怎么去?上面对角线上的元素是相等的,那么就可以想到将对角线映射到一个一维数组里,比如第一个就是-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,每次只需要判断当前点所在的对角线的一维数组下标是否等于1。一共需要3个判断-两个对角线,一个列。所以使用三个数组存放。
int vis[3][1000]; int n; int tot = 0; int c[100];
void search(int cur) {
if (cur == n) {
tot++;
for (int i = 0; i < n; i++)cout << c[i] << " ";//输出可行的排列
cout << endl;
}
else for (int i = 0; i < n; i++) {
if (!vis[0][i] && !vis[1][cur + i] && !vis[2][cur - i + n]) { //cur-i+n之所以要加n,是因为下标可能为负
c[cur] = i;
vis[0][i] = vis[1][cur + i] = vis[2][cur - i + n] = 1;//将这个点的列全置为1,对角线置为1
search(cur + 1);//搜索下一层
vis[0][i] = vis[1][cur + i] = vis[2][cur - i + n] = 0;//循环搜索第二种情况时需要将列和对角线清除
}
}
}
数学方法简单的解决了8皇后问题,主要就是简化判断。