问题:在n*n的方格棋盘上,放置n个皇后,要求每个皇后不同行,不同列,不同左右对角线。
解:皇后问题是算法中的经典问题。设f(i,n)表示在n*n方格棋盘 ,已经旋转好第1个,…,第i-1个皇后,现要放置第i个,…,第n个皇后。旋转前i个皇后的解法f(i,n)和旋转前i+1个皇后的解法f(i+1,n)是相似的。
如何在第k列上找到合适的位置旋转一个皇后呢?假设在第k列上找到合适的位置(i,k)旋转一个皇后,要求它与第1到k-1列上的皇后不同行、不同列、不同左右对角线。
1、显然不同列成立!因为前k-1个皇后分别放在第1到k-1列上,而该皇后放在第k列上;
2、对于第j(1<=j<=k-1)个皇后,其位置为(q[j],j),要求与(i,k)位置的皇后不同行的条件为i!=q[j];
3、如果设全棋盘的方格作为二维数组A[1..n,1..n]的下标那样标记,可以看到,对于在同一条对角线上的由左上方到右下方的每个元素有相同的“行-列”值。同样,在同一条对角线上的由右上方到左下方的每个元素则有相同的“行+列”的值。这样,假设有两个皇后放置在(i,k)和(q[j],j)的位置上,那么根据上述原理,仅当:i-k=q[j]-j或者i+k=q[j]+j时,它们才在同一对角线上。
对应的递归算法如下:
#include <iostream> #include <iomanip> #include <stdlib.h> using namespace std; const int N = 20; //最多放置皇后个数 int q[N]; //存放皇后所在的行号 int count = 0; //存放解的个数 void Print(int n) //输出一个解 { count++; int i; cout << ” 第” << count << ” 个解:”; for(i=1; i<=n; i++) cout << “(” << i << “,” << q[i] << “)”; cout << endl; } int Place(int i, int k) //测试第k列的i行上能否摆放皇后 { int j; j = 1; while(j < k) //j=1到k-1是已经旋转了皇后的列 { if((q[j]==i) || (abs(q[j]-i)==abs(j-k)))//第j列皇后是否在i行上。位置(q[j],j)与(i,k)是否同对角线 return 0; j++; } return 1; } void Queens(int k, int n) //第k个皇后放到第k列上 { if(k>n) Print(n); //所有皇后放置结束 else for(int i=1; i<=n; i++) //在第k列上穷举每一个位置 if(Place(i, k)) { q[k] = i; Queens(k+1, n); //放置余下的皇后 } } void main() { int n; //n存放实际皇后个数 cout << “皇后问题(n<20) n:”; cin >> n; if(n>20) cout << “n的值太大,不能求解/n”; else { cout << “皇后问题求解如下:” << endl; Queens(1,n); cout << endl; } }
递归法求解皇后问题的优点是容易想到,不难理解, 但是缺点也相当明显,耗时极多。一点也不优化。可用位运算和广度搜索算法进行优化。