问题:将8颗棋子放入一个8行8列的棋盘中,要求每颗棋子所在的行、列以及正斜线上不能有其它棋子。将所有可能的棋局输出。
显见每行且每列只能放置一颗棋子,才能满足每颗棋子所在的行、列不能有其它棋子。那么,假设去除正斜线上不能有多颗棋子这一限制条件,原问题就转化为8个元素全排列的问题。
于是我们得到了解决方案,生成所有8! 种排列并去除那些斜线上有多颗棋子的排列。这样就要生成 8! 种排列,不但计算量大,而且其中绝大部分不满足同一斜线上不能有多颗棋子这一条件。那么有没有优化的办法?
有!——那就是回溯(试错):递归的生成排列,在生成的过程中,一旦产生不满足斜线限制条件的排列则不再继续深入其子排列问题(即剪支),而是选择下一个符合的排列继续试错下去。
代码如下:
#include <stdio.h> #define N 8 int S[N] = {0,1,2,3,4,5,6,7}; /*限制条件*/ bool constraint(int end) { for(int i = 0; i <= end; i++) for(int j = i+1; j <= end; j++) if(i - j == S[i] - S[j] || j - i == S[i] - S[j]) /*在同一斜线上*/ return false; return true; } /*交换两数*/ void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; } /*输出八皇后问题的解*/ void dump(int n) { for(int i=0; i<N; i++) printf("%dth : (%d, %d) ,", n, i, S[i]); printf("\n"); } /*递归的生成排列*/ void queen8(int s) { if(s > N-1) { static int nc = 0; /*计数*/ dump(++nc); /*输出*/ return; } for(int i=s; i<N; i++) { swap(&S[s], &S[i]); if(constraint(s)) queen8(s + 1); /*若满足限制条件,继续递归子问题*/ swap(&S[s], &S[i]); } } /*主程序*/ int main() { queen8(0); }
浙公网安备 33010602011771号