這幾天突然對八皇后問題高度興趣,準備自己動手實現它,從最笨的辦法一直到用圖論實現,展示出它的進化曆程。
每個程式段我都講的很細緻。
這是第一篇:最笨的方法。歡迎探討。
By the way .八皇后有92種解法,這個程式都羅列出來了。
過幾天再用python寫個實現,估計代碼看起來會比C的實現漂亮多了。
運行結果
<br />/**<br />* The famous 8 queens problem.<br />*<br />* @file<br />* @brief<br />*The famous 8 queens problem.<br />* @authorGary Gao<garygaowork@gmail.com><br />* @date 2011/06/24<br />*<br />*/<br />/*<br />**約定:棋盤的座標是行r0-r7,列c0-c7<br />*/<br />#include <stdio.h><br />#define SUCCEEDED 0<br />#define FAILED -1<br />#define CHESSBD_WIDTH 8<br />void init_chessbd();<br />void show_chessbd();<br />void solve_8queens();<br />int test(int[],int,int[],int);<br />int get_queen_pos(int);<br />int chessbd[CHESSBD_WIDTH][CHESSBD_WIDTH];<br />/**<br />* @brief main<br />*<br />*/<br />int main(int argc,char* argv[])<br />{<br />solve_8queens();<br />return 0;<br />}<br />/**<br />* @brief顯示棋盤<br />*<br />*/<br />void show_chessbd()<br />{<br />int i,j;</p><p>for(j = 0 ; j < CHESSBD_WIDTH; j++){<br />if(j == 0){<br />printf("%8d",j);<br />}else{<br />printf("%4d",j);<br />}<br />if(j == CHESSBD_WIDTH-1){<br />printf("/n");<br />}<br />}</p><p>for(i = 0 ; i < CHESSBD_WIDTH ; i++ ){<br />printf("%4d",i);<br />for(j = 0 ; j < CHESSBD_WIDTH ; j++){<br />printf("%4d",chessbd[i][j]);<br />}<br />printf("/n/n");<br />}<br />}<br />/**<br />* @brief 解八皇后問題的主要函數<br />*<br />*/<br />void solve_8queens()<br />{<br />int ret;<br />int result = 0;<br />int i;//Row<br />int j;//Column<br />int col_start;</p><p>/*<br />**行列佔用標記,如果某行rX(列cY)已經擺放了皇后(即被佔用),<br />**則row_marks[rX](或者col_marks[cY])置1.<br />*/<br />int row_marks[CHESSBD_WIDTH]={0};//行佔用標記<br />int col_marks[CHESSBD_WIDTH]={0};//列佔用標記</p><p>ret = SUCCEEDED;//初始化ret<br />for( i = 0 ; i < CHESSBD_WIDTH ; i++){<br />/*<br />**如果是第一次進入迴圈或者<br />**上一行(r_last)能夠成功找到皇后的位置,即上一次ret返回SUCCEEDED.<br />**那麼設定col_start = 0 ,從當前行(r_cur)行的第一列開始尋找皇后位置.<br />*/<br />if(ret == SUCCEEDED){<br />col_start = 0;<br />}<br />for(j = col_start; j < CHESSBD_WIDTH;j++){<br />//測試當前位置Current(i,j)是否滿足皇后擺放條件<br />ret = test(row_marks,i,col_marks,j);</p><p>if(ret == SUCCEEDED){<br />chessbd[i][j] = 1;<br />{//設定佔用標記<br />row_marks[i] = 1;//設定第i行已經擺放了皇后<br />col_marks[j] = 1;//設定第j列已經擺放了皇后<br />}</p><p>/*<br />**以下包括在if語句塊中的code是為找出所有解法而準備的。<br />**思路:<br />**如果棋盤的最後一行條件測試成功(ret=SUCCEEDED),說明找到了一種解法.<br />**每當找出一個解法,就顯示此解法,然後設定此解"不成立",<br />**從c_cur+1開始繼續尋找新的解法。<br />*/<br />if( i == CHESSBD_WIDTH-1){<br />printf("/n@Found a solution,NO.%d/n/n",++result);<br />show_chessbd();</p><p>//因為要尋找新的解法,所以剛才的標記要清除.<br />chessbd[i][j] = 0;<br />{//設定佔用標記<br />row_marks[i] = 0;<br />col_marks[j] = 0;<br />}<br />ret = FAILED;<br />}else{<br />break;<br />}<br />}<br />}</p><p>/*<br /> *當 ret == FAILED && j == CHESSBD_WIDTH 且i == 0 時,所有的解法就尋找完畢了<br /> *不需要繼續運行了,直接跳出大迴圈.<br /> *否則,再往下就會出現段錯誤.(原因:數組下標越界)<br />*/<br />if(ret == FAILED && j == CHESSBD_WIDTH &&i == 0){<br />break;<br />}</p><p>/*<br />**Go back one step.<br />**如果當前行(r_cur)的最後一列(c7)的條件測試仍然不成立,<br />**則退回到上一行(r_last),繼續在上一行尋找新的合適皇后擺放位置。<br />*/<br />if( ret == FAILED && j == CHESSBD_WIDTH ){</p><p>//獲得上一行(r_last) queen所在的列座標<br />col_start = get_queen_pos(i-1);<br />//撤銷[上一行]佔用標記<br />{<br />row_marks[i-1] = 0;<br />col_marks[col_start] = 0;<br />chessbd[i-1][col_start] = 0;<br />}<br />//定位上一個queen.<br />//設定上一行(r_last)皇后位置的下一列(c_next)為新的測試位置<br />col_start++;<br />//因為在此輪迴圈結束時for迴圈會自動i++<br />//所以此處需i-2才能跳轉到上一行(r_last)<br />i = i-2;<br />}<br />}<br />}<br />/**<br />* @brief 測試給定的position是否滿足擺放皇后的條件.<br />*<br />* @param[in] row_marks- 行佔用標記.<br />* @param[in] r_num- 被測試的Row positon.<br />* @param[in] col_marks- 列佔用標記.<br />* @param[in] c_num- 被測試的Column positon.<br />* @return- 返回SUCCEEDED如果滿足擺放皇后的條件.否則返回FAILED.<br />*/<br />int test(<br />int row_marks[],int r_num,<br />int col_marks[],int c_num<br />)<br />{<br />int ret;<br />int i,j;</p><p>ret = SUCCEEDED;<br />//test row<br />if(row_marks[r_num] == 1 ){<br />ret = FAILED;<br />}<br />//test column<br />if(col_marks[c_num] == 1 ){<br />ret = FAILED;<br />}<br />//test主對角線<br />i = r_num-1;<br />j = c_num-1;<br />for( i,j ; i >= 0 && j >= 0 ;i--,j--){<br />if(chessbd[i][j] == 1){<br />ret = FAILED;<br />break;<br />}<br />}</p><p>//test副對角線<br />i = r_num-1;<br />j = c_num+1;<br />for(i,j ; i >= 0 && j < CHESSBD_WIDTH;i--,j++){<br />if(chessbd[i][j] == 1){<br />ret = FAILED;<br />break;<br />}<br />}<br />return ret;<br />}<br />/**<br />* @brief Get the position of queen in the specified row<br />*<br />* @param[in] row_num- Row position.<br />* @return- Column position of the queen in the specified row.<br />*/<br />int get_queen_pos(int row_num)<br />{<br />int col;<br />int j;<br />//chessbd<br />for( j = 0 ; j < CHESSBD_WIDTH; j++){<br />if( chessbd[row_num][j] == 1){<br />col = j;<br />break;<br />}<br />}<br />return col;<br />}<br />/*<br />Log20110622.1:<br />檢測符合八皇后的條件的方法有兩種,一種是最笨的掃描檢測方法,另一種是標記檢測方法。<br />目前行列檢測使用的是標記檢測方法,而主副對角線序列使用的是笨方法。[因為我還沒學會矩陣的內容]<br />Log20110622.2:<br />找出所有解法後,出現段錯誤,未知原因。<br />對debug進行了分級DEBUG_A,DEBUG_B,DEBUG_C<br />*/<br />/*<br />TASK20110622:<br />Done 找出八皇后的所有解法——92種<br />TASK20110622:<br />Done 段錯誤已經修複,段錯誤是數組越界訪問引起的。<br />最佳化八皇后解法的代碼<br />使用其他編程思路解決八皇后問題(參考,http://baike.baidu.com/view/622604.htm,)<br />TASK20110624:<br />Done 為程式添加簡單易懂的注釋<br />學習到的新技巧:<br />gcc命令中定義宏使用—D選項.<br />*/<br />