八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。 该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
解法:回溯法
#include<iostream> #include<math.h> using namespace std; int n=8; int total=0; int *c=new int(n);//c[i]==j表示这个皇后处于第i行第j列 bool is_ok(int row){ for(int j=0;j!=row;j++){ //(c[row]-c[j])/(row-j)==1||(c[row]-c[j])/(row-j)==-1 //就是数学上的斜率k=(f(x)-f(x0))/(x-x0) if(c[row]==c[j] || row-c[row]==j-c[j] || row+c[row]==j+c[j])//同列、同副对角线、同主对角线 return false; } return true; } void queen(int row){//按行进行选择八皇后的位置 if(row==n){ total++; } else for(int col=0;col!=n;col++){ c[row]=col; if(is_ok(row)){ queen(row+1);//继续摆放下一行的皇后 } } } int main(){ queen(0); cout<<total; free(c); return 0; }
维基百科的解法:
#include <stdio.h> #define QUEENS 8 /*皇后数量*/ #define IS_OUTPUT 1 /*(IS_OUTPUT=0 or 1),Output用于选择是否输出具体解,为1输出,为0不输出*/ int A[QUEENS + 1], B[QUEENS * 3 + 1], C[QUEENS * 3 + 1], k[QUEENS + 1][QUEENS + 1]; int inc, *a = A, *b = B + QUEENS, *c = C; void lay(int i) { int j = 0, t, u; while (++j <= QUEENS) if (a[j] + b[j - i] + c[j + i] == 0) { k[i][j] = a[j] = b[j - i] = c[j + i] = 1; if (i < QUEENS) lay(i + 1); else { ++inc; if (IS_OUTPUT) { for (printf("(%d)\n", inc), u = QUEENS + 1; --u; printf("\n")) for (t = QUEENS + 1; --t; ) k[t][u] ? printf("Q ") : printf("+ "); printf("\n\n\n"); } } a[j] = b[j - i] = c[j + i] = k[i][j] = 0; } } int main(void) { lay(1); printf("%d皇后共计%d个解\n", QUEENS, inc); return 0; }
回溯法(非递归)
#include<stdio.h> #define PRINTF_IN 1 //定义是否打印,1:打印,0:不打印 int queens(int Queens){ int i, k, flag, not_finish=1, count=0; //正在处理的元素下标,表示前i-1个元素已符合要求,正在处理第i个元素 int a[Queens+1]; //八皇后问题的皇后所在的行列位置,从1幵始算起,所以加1 i=1; a[1]=1; //为数组的第一个元素赋初值 printf("%d皇后的可能配置是:",Queens); while(not_finish){ //not_finish=l:处理尚未结束 while(not_finish && i<=Queens){ //处理尚未结束且还没处理到第Queens个元素 for(flag=1,k=1; flag && k<i; k++) //判断是否有多个皇后在同一行 if(a[k]==a[i]) flag=0; for (k=1; flag&&k<i; k++) //判断是否有多个皇后在同一对角线 if( (a[i]==a[k]-(k-i)) || (a[i]==a[k]+(k-i)) ) flag=0; if(!flag){ //若存在矛盾不满足要求,需要重新设置第i个元素 if(a[i]==a[i-1]){ //若a[i]的值已经经过一圈追上a[i-1]的值 i--; //退回一步,重新试探处理前一个元素 if(i>1 && a[i]==Queens) a[i]=1; //当a[i]为Queens时将a[i]的值置1 else if(i==1 && a[i]==Queens) not_finish=0; //当第一位的值达到Queens时结束 else a[i]++; //将a[il的值取下一个值 }else if(a[i] == Queens) a[i]=1; else a[i]++; //将a[i]的值取下一个值 }else if(++i<=Queens) if(a[i-1] == Queens ) a[i]=1; //若前一个元素的值为Queens则a[i]=l else a[i] = a[i-1]+1; //否则元素的值为前一个元素的下一个值 } if(not_finish){ ++count; if(PRINTF_IN){ printf((count-1)%3 ? " [%2d]:" : "\n[%2d]:", count); for(k=1; k<=Queens; k++) //输出结果 printf(" %d", a[k]); } if(a[Queens-1]<Queens ) a[Queens-1]++; //修改倒数第二位的值 else a[Queens-1]=1; i=Queens -1; //开始寻找下一个满足条件的解 } } return count; } int main() { int Num ; printf("输入一个N皇后数值:"); scanf("%d" , &Num); printf("\n%d皇后有%d种配置\n",Num,queens(Num)); }