题目:在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即要求任意两个皇后不得处在同一行、同一列或者同一对角斜线上。求出总共有多少种摆法。
思路一:
对于不在同行且不在同列的摆法,8行每行各有一个皇后,那么就是在8列中进行全排列,很明显有8!种情况。在这8!中情况中,再排除对角斜线上在出现冲突的情况。
对于排列组合类型的问题,经常采用递归方式来解决。
数据结构:因为每行注定只能有1个皇后,因此我们采用一维数组,只保存皇后的所在的列号:int ColunmIndex[8]; 数组的序号代表皇后所在的行号,对应元素代表皇后所在的列号。
需要解决的问题有:
1、如何判断一个ColunmIndex是否满足不在对角斜线上冲突?
观察发现,位于矩阵中同一斜线上的两个元素有这样的特点:行号的差和列号的差的绝对值一致。
对于正斜线,行号的差=列号的差;对于反斜线,行号的差= -列号的差。
2、如何得到全排列组合各种情况的ColunmIndex?
排列的生成是组合数学中的一个内容。这一数量级的问题经常使用递归。排列组合的各种情况可以通过两两交换的方式得到。首先我们可以把数组的第1个元素分别和每个元素两两交换,这样会得到8中排列形式(包括自己和自己交换的情况),这样每个数都有机会做1号元素了;这8种情况下分别在此基础之上(下面的问题看作8个递归子问题),再把第2个元素和其之后的每个元素(包括第2个元素自己)两两交换,各得到7种排列形式,8种情况下就有8*7种排列形式,这样每个数都有机会做2号元素了…………就是这样的依次的方法,得到了8!种不同的排列形式。
这种产生全排列数据的递归方式是非常值得学习和复用的。
int data[N]={0,1,2,3,4,5,6,7,8}; 调用ProduceArray(data, 0); void ProduceArray(int data[], int index) { int i; if(index < N) { for(i=index;i<N;i++) { swap(data[i], data[index]); ProduceArray(data, index+1); swap(data[i], data[index]); } } else { 加入处理逻辑 } }
完整代码:
#include <stdio.h> int num = 0; const int N = 8; int ColunmIndex[N]; void printArray(int data[]) { int i; num++; printf("No.%d : \n",num); for(i=0;i<N;i++) printf("%d ",data[i]); printf("\n"); } bool check(int data[]) { int i,j; for(i=0;i<N;i++) { for(j=i+1;j<N;j++) { if( ((i-j)==(data[i]-data[j]))||((j-i)==(data[i]-data[j]))) return false; } } return true; } void swap(int &a, int &b) { int tmp = a; a = b; b = tmp; } void ProduceArray(int data[], int index) { int i; if(index < N) { for(i=index;i<N;i++) { swap(data[i], data[index]); ProduceArray(data, index+1); swap(data[i], data[index]); } } else { if(check(data)) printArray(data); } } void EightQueen() { int i; for(i=0;i<N;i++) ColunmIndex[i] = i; ProduceArray(ColunmIndex, 0); } int main() { EightQueen(); return 1; }
思路二:由小到大逐渐扩大的递归。一行一行的扩展状态,每次扩展都既保证之前所有行都不冲突,又保证所有的情况都考虑进来。
代码:
#include <stdio.h> #include <stdlib.h> int num = 0; const int N = 8; int ColunmIndex[N]; void printArray(int data[]) { int i; num++; printf("No.%d : \n",num); for(i=0;i<N;i++) printf("%d ",data[i]); printf("\n"); } bool check(int row) { for(int i=0;i<row;i++) { int diff = abs(ColunmIndex[i]-ColunmIndex[row]); if(diff == 0 || diff == row - i) return false; } return true; } void placeQueen(int row) { if(row == N) { printArray(ColunmIndex); return; } for(int i=0;i<N;i++) { ColunmIndex[row] = i; if(check(row)) placeQueen(row+1); } } int main() { placeQueen(0); return 1; }