八皇后問題各種解法分析
阿新 • • 發佈:2019-01-24
遞迴與回溯:
a.回溯演算法的基本思想:從問題的某一種狀態出發,搜尋可以到達的所有狀態。當某個狀態到達後,可向前回退,並繼續搜尋其他可達狀態。當所有狀態都到達後,回溯演算法結束! b.對於回溯演算法,在前面KMP匹配中就利用了這個思想,只不過當時KMP中定義了一個node陣列(起到了一個地圖的作用,記錄了每種回溯情況的可能)。而這節中,是利用函式的活動物件儲存回溯演算法的狀態資料,因此可以利用遞迴完成回溯演算法!2.八皇后問題:
a.問題背景:國際象棋是一個8*8的矩陣,在棋盤中同時放下8個皇后,且互相不攻擊的情況叫八皇后問題 b.如圖,說明八皇后問題中的回溯演算法:注意:其實就是不斷的通過遞迴函式,去往棋盤中嘗試放皇后,成功就繼續遞迴(即繼續放皇后),失敗就跳出遞迴函式,回溯到上層遞迴函式中,上層遞迴函式中儲存著上一個皇后的位置!!!這就是八皇后中,回溯的概念! c.八皇后的演算法思路: 第一、為了更加方便我們表示棋盤,我們使用一個10*10的二維陣列來表示8*8的棋盤加棋盤邊框。
- char board[10][10];
- void init()
- {
- int i = 0;
- int j = 0;
-
for
- {
- board[0][i] = '#';
- board[N+1][i] = '#';
- board[i][0] = '#';
- board[i][N+1] = '#';
- }
- for(i=1; i<=N; i++)
- {
- for(j=1; j<=N; j++)
- {
- board[i][j] = ' ';
- }
- }
- }
-
void
- {
- int i = 0;
- int j = 1;
- for(i=0; i<N+2; i++)
- {
- for(j=0; j<N+2; j++)
- {
- printf("%c", board[i][j]);
- }
- printf("\n");
- }
- }
- typedefstruct _tag_pos //定義一個數據結構來充當方向
- {
- int i;
- int j;
- }Pos;
- /*檢測三個方向 左上 右上 正上 橫排是不檢測的 因為一排只放一個*/
- static Pos pos[3] = {{-1,-1},{-1,1},{-1,0}};
- int check(int i, int j)
- {
- int p = 0;
- int ret = 1;
- for(p = 0; p < 3; p++) //檢測三個方向
- {
- int ni = i;
- int nj = j;
- while(ret && (board[ni][nj] != '#'))//判斷沒有到達棋盤邊界
- {
- ni = ni + pos[p].i;
- nj = nj + pos[p].j;
- ret = ret && (board[ni][nj] != '*');//判斷這個方向沒有放過皇后
- }
- }
- return ret; //可以放皇后返回1 不可返回0
- }
- void find(int i)
- {
- int j = 0;
- if(i > N) //判斷是否已經超過了第八行
- {
- coust++; //計算八皇后情況的個數
- display();
- //getchar();
- }
- else
- {
- for(j = 1; j <= N; j++) //判斷一行 是否有匹配的位置
- {
- if(check(i,j))
- {
- board[i][j] = '*'; //放置皇后
- find(i+1);
- board[i][j] = ' '; //清除放錯的皇后
- }
- }
- }
- }
看著上面的動態想象下,當有時候,出現過的皇后又消失了,就是因為for迴圈結束了,還沒有check成功,導致遞迴結束,返回到上層遞迴,並且擦除上層皇后的位置,並且繼續for迴圈,如果這層for中check依然不成功,繼續回溯,這個消失的過程就是回溯過程!與動圖不同的是,我們的程式,不會在找完一種情況後就結束!即使成功匹配,程式也會向上層回溯,去尋找其它的情況,直到第一行for迴圈結束(即 i=1 的時候),程式才會停止!
本節程式碼:
八皇后問題的完整程式碼:- #include <stdio.h>
- #define N 8
- staticchar board[N+2][N+2];
- staticint coust = 0; //記錄八皇后個數
- typedefstruct _tag_pos //定義一個數據結構來充當方向
- {
- int i;
- int j;
- }Pos;
- /*檢測三個方向 左上 右上 正上 橫排是不檢測的 因為一排只放一個*/
- static Pos pos[3] = {{-1,-1},{-1,1},{-1,0}};
- void init()
- {
- int i = 0;
- int j = 0;
- for(i=0; i<N+2; i++)
- {
- board[0][i] = '#';
- board[N+1][i] = '#';
- board[i][0] = '#';
- board[i][N+1] = '#';
- }
- for(i=1; i<=N; i++)
- {
- for(j=1; j<=N; j++)
- {
- board[i][j] = ' ';
- }
- }
- }
- void display()
- {
- int i = 0;
- int j = 1;
- for(i=0; i<N+2; i++)
- {
- for(j=0; j<N+2; j++)
- {
- printf("%c ", board[i][j]);
- }
- printf("\n");
- }
- }
- int check(int i, int j)
- {
- int p = 0;
- int ret = 1;
- for(p = 0; p < 3; p++) //檢測三個方向
- {
- int ni = i;
- int nj = j;
- while(ret && (board[ni][nj] != '#'))//判斷沒有到達棋盤邊界
- {
- ni = ni + pos[p].i;
- nj = nj + pos[p].j;
- ret = ret && (board[ni][nj] != '*');//判斷這個方向沒有放過皇后
- }
- }
- return ret; //可以放皇后返回1 不可返回0
- }
- void find(int i)
- {
- int j = 0;
- if(i > N) //判斷是否已經超過了第八行