算法提出:
在国际象棋棋盘上(8*8)放置八个皇后,使得任意两个皇后之间不能在同一行,同一列,也不能位于同于对角线上。问共有多少种不同的方法,并且指出各种不同的放法。
算法思路:
首先我们分析一下问题的解,我们每取出一个皇后,放入一行,共有八种不同的放法,然后再放第二个皇后,同样如果不考虑规则,还是有八种放法。于是我们可以用一个八叉树来描述这个过程。从根节点开始,树每增加一层,便是多放一个皇后,直到第8层(根节点为0层),最后得到一个完全八叉树。
紧接着我们开始用深度优先遍历这个八叉树,在遍历的过程中,进行相应的条件的判断。以便去掉不合规则的子树。
那么具体用什么条件来进行子树的裁剪呢?
我们先对问题解的结构做一个约定。
用X[i]来表示,在第i行,皇后放在了X[i]这个位置。
于是我们考虑第一个条件,不能再同一行,同一列于是我们得到x[i]不能相同。剩下一个条件是不能位于对角线上,这个条件不是很明显,我们经过分析得到,设两个不同的皇后分别在j,k行上,x[j],x[k]分别表示在j,k行的那一列上。那么不在同一对角线的条件可以写为abs((j-k))!=abs(x[j]-x[k]),其中abs为求绝对值的函数。
于是下面我们便可以利用一个递归的调用来遍历八叉树。
我们首先定义一个访问某节点所有子节点的函数
void backtrack(int t){ if(t>num) //num为皇后的数目 { sum++;//sum为所有的可行的解 for(int m = 1;m
下面我们定义了条件判断函数
bool place(int k){ for(int j = 1;j
上面的函数便是按照我们上面说介绍的条件进行判断。
最后就是主程序的调用了
static int num;static int *x;static int sum;void main(){ num = 8; sum = 0; x = new int[num+1]; for(int i= 0;i<=num;i++) x[i] = 0; backtrack(1); cout<<"方案共有"<
一个更加简便的解法:
使用1 - 8的全排列进行依次判断,判断只需要考虑每种排列的斜线是否会被攻击!
bool isPass(int*);int EightQueue() { int a[8] = { 0,1, 2, 3, 4, 5, 6, 7 }; int n = 0; while (next_permutation(a, a + 8)) {//一一判断排列是否可行 if (isPass(a))++n;//可行就计数一次 } return n;}bool isPass(int *a) {//判断斜方是否会受到其他皇后的攻击 int queue[8][8] = { 0 }; for(int i=0;i<8;++i) queue[i][a[i]] = 1;//按照0-7数字,每行按照a中数字对应其列进行放置皇后,则不会出现同行同列出现两个皇后了,大大减少了判断 for (int i = 0; i < 8; ++i) { for (int t = i - 1, k = a[i] - 1; t >= 0 && k >= 0; --t, --k) {//左上方 if (queue[t][k] == 1) return false; } for (int t = i + 1, k = a[i] + 1; t < 8 && k < 8; ++t, ++k) {//右下方 if (queue[t][k] == 1) return false; } for (int t = i + 1, k = a[i] - 1; t < 8 && k >= 0; ++t, --k) {//左下方 if (queue[t][k] == 1) return false; } for (int t = i - 1, k = a[i] + 1; t >= 0 && k < 8; --t, ++k) {//右上方 if (queue[t][k] == 1) return false; } } return true;}