回溯法解决八皇后问题
1.“八皇后”问题
八皇后问题是十九世纪著名数学家高斯于1850年提出的。问题是:在8*8的棋盘上摆放8个皇后,使其不能互相攻击,即任意的两个皇后不能处在同一行,同一列,或同一斜线上。可以把八皇后问题拓展为n皇后问题,即在n*n的棋盘上摆放n个皇后,使其任意两个皇后都不能处于同一行、同一列或同一斜线上。
2.冲突条件判断
如下图,方块内的值(i,j)表示第i行第j列。(1,1)位置如果放了皇后,那么(1,1)位置所在的行,列,斜线上都不能再放皇后。
如何判断两个位置冲突呢?
(1)判断两个位置是否再斜线上:根据斜率
A(1,1)位置,B(2,0)位置和C(2,2)位置,A和B冲突:(0-1)/(2-1)==-1,A和C冲突:(2-1)/(2-1)==1。
因此,对于任意两点A(x1,y1)和B(x2,y2),两点斜线冲突满足条件:abs((y2-y1)/(x2-x1))==1,
该公式转换为:abs(y2-y1)== abs(x2-x1)
(2)判断两个位置是否在同一列/同一行
任意两点A(x1,y1)和B(x2,y2)在同一列满足条件:y1 == y2,在同一行满足条件:x1 == x2
判断冲突代码如下:
int valid(int row, int col) //判断第row行第col列是否可以放置皇后 { int i; for (i = 0; i < QUEEN; ++i) //对已得到的解搜索,看是否冲突 { //判断列冲突与斜线上的冲突 //表示第i行第col列放了皇后,(row - i) / (col - arr[i]) != 1 或 -1 // arr[i] = k表示第i行第k列放了皇后 if (a[i] == col || abs(i - row) == abs(a[i] - col)) return 0; } return 1; }
3.进入正题--回溯法解决
(1)初始化棋盘
#define INITIAL -10000 //棋盘的初始值
const int QUEEN = 8;
int a[QUEEN];//表示解空间a[1]=2表示[1,2]位置有皇后 void init()//初始化 { int *p; for(p=a;p<a+QUEEN;++p) *p = INITIAL; }
(2)非递归回溯
void queen() //N皇后程序 { int n = 0; int i = 0, j = 0;//i表示行,j表示每行的列 while (i < QUEEN)//满足继续搜索的条件 { ///没有到叶子结点情况下就继续搜索 while (j < QUEEN) //对i行的每一列进行探测,看是否可以放置皇后 { if(valid(i, j)) //该位置可以放置皇后 { a[i] = j; //第i行放置皇后,位置位j列 j = 0; //第i行放置皇后以后,需要继续探测下一行的皇后位置, //所以此处将j清零,从下一行的第0列开始逐列探测 break; } else { ++j; //继续探测下一列 } } ///搜索到了叶子结点但是无解,直接回溯到上一行 if(a[i] == INITIAL) //第i行没有找到可以放置皇后的位置 { if (i == 0) //回溯到第一行,仍然无法找到可以放置皇后的位置, //则说明已经找到所有的解,程序终止 break; else //没有找到可以放置皇后的列,此时就应该回溯 { --i; j = a[i] + 1; //把上一行皇后的位置往后移一列 a[i] = INITIAL; //把上一行皇后的位置清除,重新探测 continue; } } //找到解 if (i == QUEEN - 1) //最后一行找到了一个皇后位置, //说明找到一个结果,打印出来 { printf("answer %d : \n", ++n); print(); //不能在此处结束程序,因为我们要找的是N皇后问题的所有解, //此时应该清除该行的皇后,从当前放置皇后列数的下一列继续探测。 j = a[i] + 1; //从最后一行放置皇后列数的下一列继续探测 a[i] = INITIAL; //清除最后一行的皇后位置 continue; } ++i; //继续探测下一行的皇后位置 } }
(3)结果打印
void print() //打印输出N皇后的一组解 { int i, j; for (i = 0; i < QUEEN; ++i) { for (j = 0; j < QUEEN; ++j) { if (a[i] != j) //a[i]为初始值 printf("%c ", '.'); else //a[i]表示在第i行的第a[i]列可以放置皇后 printf("%c ", '#'); } printf("\n"); } for (i = 0; i < QUEEN; ++i) printf("%d ", a[i]); printf("\n"); printf("--------------------------------\n"); }