昨天在同济医学院参加DeNA宣讲及笔试,最后一题为八皇后递归求解填空,不懂。
今天在上网搜索并认真思考后,在此做一个总结。
求解思路:
棋盘为8行8列,从第0行开始放第一个皇后,设放在第 i 列,接下来,
逐个放其余7行,这个前提保证所有皇后均不在同一行。
还需保证所有皇后不在同一列,这需要用到数组 col[i],0 <= i <= 7,初始化为0,表示没有任何一列放了皇后。
因为我们已在第 0 行的第 i 列放了皇后,我们添加标记 col[i] = 1,表示放其余7行时,第 i 列不能再放皇后。
同时,所有皇后不能在同一斜线上,分两种,斜率为 1 和 -1。如下图所示:
斜率为 1(positive diagonal,正斜线) 即蓝色斜线,这条斜线上的所有方格,行号和列号的和相同;
斜率为 -1(negative diagonal,负斜线) 即绿色斜线,这条斜线上的所有方格,行号和列号的差相同。
为了表示上述两种情况,用数组pd[15],nd[15]表示。
// 因为 row+col 最大为14,同时 row-col 存在 -7 的情况,实际处理时,用 row- col+7,最大也为14
// 所以数组大小均定为 15
因为我们已在第 0 行的第 i 列放了皇后,我们还需添加标记:pd[ 0+i ] = 1,nd[ 0-i+7 ] = 1。
递归实现:
由EightQueen( row = 0 )开始,表示放第0行。
递归终止条件1:当 row == 8 时,得到一个解 count++ ; return; // 解的数目加1
放每一行时,起初均有8列可放,即for( int i=0; i < 8; i++ ),但需排除被标记的列。
如果某一列被标记,那么该列不可放,需跳过这列continue。(递归终止条件2:所有列均被标记,本轮尝试失败)
选好所放的列后,作标记 col[ i ] = pd[ row+i ] = nd[ row-i+7 ] = 1,然后递归放下一行EightQueen( row+1 )。
当第 row+1 行的递归退出后,第 row 行将换下一列尝试,尝试之前,需将先前本行尝试的列的标记清除,即:
col[ i ] = pd[ row+i ] = nd[ row-i+7 ] = 0。
代码:
#include<stdio.h>
int col[8], pd[15], nd[15], count;
void init()
{
int i;
count = 0;
for( i=0; i<8; i++) col[i] = 0;
for( i=0; i<15; i++) pd[i] = nd[i] = 0;
}
void EightQueen( int row ) {
if( row>=8 ) { count++; return; }
int i;
for( i=0; i<8; i++ ) {
if( col[i] || pd[row+i] || nd[row-i+7] ) continue;
col[i] = pd[row+i] = nd[row-i+7]=1;
EightQueen( row+1 );
col[i] = pd[row+i] = nd[row-i+7]=0;
}
}
int main() {
init();
EightQueen( 0 );
printf("八皇后解的数目为:%d\n",count);
return 0;
}