问题:
如何能够在 n×n 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。
分析:
我们可以用一串数字来表示问题的解,比如[2,4,1,3] 表示 4×4 棋盘的4皇后问题的解,第一行的棋子摆在第2列;第二行的棋子摆在第4列,依此类推
这里将用回溯法进行解题,可以看出,某一行的棋子摆了之后,棋子的那一列肯定不能再摆其他的棋子了,
所以解[x,x,x,……,x] 一定是 1-n 的一个全排列,
所以这里的解空间树我们选择排列树。
思路:
既然已经确定了要用排列树来作为问题的解空间树,接下来要确定的就是剪枝函数了。
剪枝函数只要从已经摆好的棋子的第一个开始遍历,依此判断下面的棋子是否在其攻击范围内即可,
因为我们之前没有在同一行摆多个棋子,也没有在同一列摆多个棋子,所以只要判断其他棋子是否在其斜线上的攻击范围即可,比如
第一行的棋子摆在第3列上,只要沿图中方向判断即可
for (int i = 1; i <= n-1; i++) { for (int j = i + 1; j <= n; j++) { int left = -(j - i);//向左的斜线
int right = (j - i);//向右的斜线
if (pieces[j] == pieces[i] + left||pieces[j] == pieces[i] + right) {//第i行皇后和第j行皇后会互相攻击
return false; } } }
代码:
这样摆好所有棋子后即可输出,代码如下:
#include<iostream> #include<ctime>
using namespace std; bool isOK(int n, int pieces[]) { //剪枝函数 //判断当前状态是否合理,即皇后会不会互相攻击
for (int i = 1; i <= n-1; i++) { for (int j = i + 1; j <= n; j++) { int left = -(j - i);//向左的斜线
int right = (j - i);//向右的斜线
if (pieces[j] == pieces[i] + left||pieces[j] == pieces[i] + right) {//第i行皇后和第j行皇后会互相攻击
return false; } } } //所有皇后都不会互相攻击
return true; } void swap(int &a, int &b) { int t = a; a = b; b = t; } void nQueen(int n, int t, int pieces[]) { if (t > n) { for (int i = 1; i <= n; i++) { for (int j = 1; j < pieces[i]; j++) cout << "- "; cout << pieces[i]<<" "; for (int j = pieces[i] + 1; j <= n; j++) cout << "- "; cout << endl; } cout << endl; } else { for (int i = t; i <= n; i++) { swap(pieces[t], pieces[i]); if (isOK(t, pieces)) { nQueen(n, t + 1, pieces); } swap(pieces[t], pieces[i]); } } } void main() { int n; cin >> n; int *pieces = new int[n + 1]; for (int i = 1; i <= n; i++) { pieces[i] = i; } nQueen(n, 1, pieces); cout << "OK" << endl; system("pause"); }