现在的位置: 首页 > 综合 > 正文

回溯 八皇后问题(递归和非递归)

2012年06月26日 ⁄ 综合 ⁄ 共 1542字 ⁄ 字号 评论关闭

8皇后问题:如何在8 x 8的国际象棋棋盘上安排8个皇后,使得没有两个皇后能互相攻击?如果两个皇后处在同一行、同一列或同一条对角线上,则她们能互相攻击。)

解向量为长度为8的数组,记为solution。因为共有8个皇后,而棋盘刚好为8*8,所以每一行肯定会有一个皇后,那么我们约定solution[i]表示第i+1个皇后放在第i+1行的第几列。solution中的列从1开始记。

回溯算法的思路是对问题的状态空间树进行深度优先搜索,树的每一层节点代表了对解的每一个分量所做的选择。如果该节点表示的选择符合要求,那么继续向下搜索,否则,尝试下一个选择。如果该层的所有选择都已经尝试过,那么我们向上回溯。

递归版本:

 void eight_queue_recursive( int *solution, int dimension, int row )
 {//dimension表示棋盘的维数,row表示放置第几行的皇后
 
        if( row > dimension ) return;
       	int i;
        for( i = 0; i < dimension; ++i )//尝试每一列
        {
                solution[row-1] = i+1;
                if( if_valid( solution, row ) )//如果合法
                {
                         if( row < dimension )//还有皇后没放
                                 eight_queue_recursive( solution, dimension, row+1 );
                        else
                                 print( solution, dimension );
                }
                solution[row-1] = 0;
         }
 }

一般的回溯算法可以描述如下:

backtrack(k)
for 每个x∈Xk
	if x is valid 
 	xk←x;将xk加入v
 	if v 为最终解then print the solution
 	else if v 是部分解then backtrack(k+1)
end for

迭代版本:

void eight_queue( int *solution, int dimension )
{
        int k = 0;//k表示第几个皇后
        while( k >= 0 )//要判断k是否>0
        {   
                while( solution[k] < dimension )//判断k是否会超过dimension
                {   
                        solution[k]+=1;
                        if( if_valid( solution, k+1 ) ) 
                        {   
                                if( k < (dimension-1) )
                                        ++k;
                                else
                                        print( solution, dimension );
                        }   
                }   
                solution[k] = 0;//回溯
                --k;
        }   
}

在判断某个位置是否可以放皇后时,需要注意如下规律:

 1.从左上角到右下角的主对角线或其平行线(斜率-1)上,下标之差(行号-列号)相  
 等。
 2.斜率为+1的斜线上,元素的2个下标之和(行号+列号)值相等。
 即若两个皇后的位置分别为(i,j)和(k,l),且i-j=k-l 或 i+j = k+l,那么这两个皇后位于一条斜线上。上面两个方程又和转化为i-k = j-l 和 i-k = l-j
 即只要|i-k| = |l-j|,那么这两个皇后在一条斜线上,位置冲突.运用上面的规律,if_valid会大大简化。

今天上午写了非递归的,运行的时候竟然有几千个解,很纳闷,而且是重复着输出。调试了半天,原来是最外层的while循环条件应该是k>=0,最初写成了 k<dimension,但是这样也不应该输出几千个解啊,当k==-1时应该直接就崩溃了啊,所以应该是输出第92个结果后应该就段错误啊。这个想法害我不浅啊,怎么也看不出来什么错。最后就尝试输出solution[-1],原以为会出现错误,结果真的输出来了啊。原来c语言中数组越界访问程序不是直接就崩溃的,以后写程序要注意了。

   上面的程序同样适用于N皇后问题,试了一下,把8换成18后,程序竟然可以得到几十万个解,当然程序没跑完我就给停了。

抱歉!评论已关闭.