这几天突然对八皇后问题很感兴趣,准备自己动手实现它,从最笨的办法一直到用图论实现,展示出它的进化历程。 每个程序段我都讲的很细致。 这是第一篇:最笨的方法。欢迎探讨。
By the way .八皇后有92种解法,这个程序都罗列出来了。
过几天再用python写个实现,估计代码看起来会比C的实现漂亮多了。
运行结果如图:
/** * The famous 8 queens problem. * * @file * @brief * The famous 8 queens problem. * @author Gary Gao <[email protected]> * @date 2011/06/24 * */ /* ** 约定:棋盘的坐标是行r0-r7,列c0-c7 */ #include <stdio.h> #define SUCCEEDED 0 #define FAILED -1 #define CHESSBD_WIDTH 8 void init_chessbd(); void show_chessbd(); void solve_8queens(); int test(int[],int, int[],int); int get_queen_pos(int); int chessbd[CHESSBD_WIDTH][CHESSBD_WIDTH]; /** * @brief main * */ int main(int argc,char* argv[]) { solve_8queens(); return 0; } /** * @brief 显示棋盘 * */ void show_chessbd() { int i,j; for(j = 0 ; j < CHESSBD_WIDTH; j++){ if(j == 0){ printf("%8d",j); }else{ printf("%4d",j); } if(j == CHESSBD_WIDTH-1){ printf("/n"); } } for(i = 0 ; i < CHESSBD_WIDTH ; i++ ){ printf("%4d",i); for(j = 0 ; j < CHESSBD_WIDTH ; j++){ printf("%4d",chessbd[i][j]); } printf("/n/n"); } } /** * @brief 解八皇后问题的主要函数 * */ void solve_8queens() { int ret; int result = 0; int i; //Row int j; //Column int col_start; /* ** 行列占用标记,如果某行rX(列cY)已经摆放了皇后(即被占用), ** 则row_marks[rX](或者col_marks[cY])置1. */ int row_marks[CHESSBD_WIDTH]={0}; //行占用标记 int col_marks[CHESSBD_WIDTH]={0}; //列占用标记 ret = SUCCEEDED; //初始化ret for( i = 0 ; i < CHESSBD_WIDTH ; i++){ /* **如果是第一次进入循环或者 **上一行(r_last)能够成功找到皇后的位置,即上一次ret返回SUCCEEDED. **那么设置col_start = 0 ,从当前行(r_cur)行的第一列开始寻找皇后位置. */ if(ret == SUCCEEDED){ col_start = 0; } for(j = col_start; j < CHESSBD_WIDTH;j++){ //测试当前位置Current(i,j)是否满足皇后摆放条件 ret = test(row_marks,i,col_marks,j); if(ret == SUCCEEDED){ chessbd[i][j] = 1; { //设置占用标记 row_marks[i] = 1; //设置第i行已经摆放了皇后 col_marks[j] = 1; //设置第j列已经摆放了皇后 } /* **以下包括在if语句块中的code是为找出所有解法而准备的。 **思路: ** 如果棋盘的最后一行条件测试成功(ret=SUCCEEDED),说明找到了一种解法. ** 每当找出一个解法,就显示此解法,然后设置此解"不成立", ** 从c_cur+1开始继续寻找新的解法。 */ if( i == CHESSBD_WIDTH-1){ printf("/n@Found a solution,NO.%d/n/n",++result); show_chessbd(); //因为要寻找新的解法,所以刚才的标记要清除. chessbd[i][j] = 0; { //设置占用标记 row_marks[i] = 0; col_marks[j] = 0; } ret = FAILED; }else{ break; } } } /* *当 ret == FAILED && j == CHESSBD_WIDTH 且i == 0 时,所有的解法就寻找完毕了 *不需要继续运行了,直接跳出大循环. *否则,再往下就会出现段错误.(原因:数组下标越界) */ if(ret == FAILED && j == CHESSBD_WIDTH &&i == 0){ break; } /* **Go back one step. **如果当前行(r_cur)的最后一列(c7)的条件测试仍然不成立, **则退回到上一行(r_last),继续在上一行寻找新的合适皇后摆放位置。 */ if( ret == FAILED && j == CHESSBD_WIDTH ){ //获得上一行(r_last) queen所在的列坐标 col_start = get_queen_pos(i-1); //撤销[上一行]占用标记 { row_marks[i-1] = 0; col_marks[col_start] = 0; chessbd[i-1][col_start] = 0; } //定位上一个queen. //设置上一行(r_last)皇后位置的下一列(c_next)为新的测试位置 col_start++; //因为在此轮循环结束时for循环会自动i++ //所以此处需i-2才能跳转到上一行(r_last) i = i-2; } } } /** * @brief 测试给定的position是否满足摆放皇后的条件. * * @param[in] row_marks - 行占用标记. * @param[in] r_num - 被测试的Row positon. * @param[in] col_marks - 列占用标记. * @param[in] c_num - 被测试的Column positon. * @return - 返回SUCCEEDED如果满足摆放皇后的条件.否则返回FAILED. */ int test( int row_marks[],int r_num, int col_marks[],int c_num ) { int ret; int i,j; ret = SUCCEEDED; //test row if(row_marks[r_num] == 1 ){ ret = FAILED; } //test column if(col_marks[c_num] == 1 ){ ret = FAILED; } //test主对角线 i = r_num-1; j = c_num-1; for( i,j ; i >= 0 && j >= 0 ;i--,j--){ if(chessbd[i][j] == 1){ ret = FAILED; break; } } //test副对角线 i = r_num-1; j = c_num+1; for(i,j ; i >= 0 && j < CHESSBD_WIDTH;i--,j++){ if(chessbd[i][j] == 1){ ret = FAILED; break; } } return ret; } /** * @brief Get the position of queen in the specified row * * @param[in] row_num - Row position. * @return - Column position of the queen in the specified row. */ int get_queen_pos(int row_num) { int col; int j; //chessbd for( j = 0 ; j < CHESSBD_WIDTH; j++){ if( chessbd[row_num][j] == 1){ col = j; break; } } return col; } /* Log20110622.1: 检测符合八皇后的条件的方法有两种,一种是最笨的扫描检测方法,另一种是标记检测方法。 目前行列检测使用的是标记检测方法,而主副对角线序列使用的是笨方法。[因为我还没学会矩阵的内容] Log20110622.2: 找出所有解法后,出现段错误,未知原因。 对debug进行了分级DEBUG_A,DEBUG_B,DEBUG_C */ /* TASK20110622: Done 找出八皇后的所有解法——92种 TASK20110622: Done 段错误已经修复,段错误是数组越界访问引起的。 优化八皇后解法的代码 使用其他编程思路解决八皇后问题(参考,http://baike.baidu.com/view/622604.htm,) TASK20110624: Done 为程序添加简单易懂的注释 学习到的新技巧: gcc命令中定义宏使用—D选项. */