八皇后問題之回溯法

八皇后問題之回溯法

八皇后問題

將n個皇后放置在n*n的國際象棋棋盤上,其中沒有任何兩個皇后處於同一行,同一列或者同一對角線上,以使得的它們不能相互攻擊。ios

問題分析
  • 最簡答的思路是把問題轉化爲「從64個格子中選一個子集」,使得「子集中剛好有8個格子,且任意選出兩個格子都不在同一行,同一列或者贊成對角線上」。這剛好是子集枚舉問題。然而,64個格子的子集有2^64個,太大了,則並非一個很好的模型。
  • 咱們把思路轉化爲「從64個格子中選出8個格子」,這是一個明顯的組合問題,根據排列組合,有C8 64=4.426*10^9種結果,雖然比第一種要好,可是性能依然不夠好。
  • 最後咱們經過思考,能不能將它裝換爲一維問題:由於每行每列各放置一個皇后,若是用C[X]表示第X行皇后的列編號,則問題會變成全排列生成問題,而0-7的排列一共只有8!=40320個,枚舉的次數不會超過這個值。

若是這樣表示的話,那麼判斷條件應該如何表示呢?算法

  • 咱們能夠先用二維數組表示一個八宮格,假設每一個格子的座標(x,y)是二維數組的下標,那麼格子(x,y)的y-x的值就可以標識出租對角線,全部的主對角線的y-x的值是相等的,同理格子(x,y)的x+y值就能表示出副對角線,那麼全部的問題就都解決了。
回溯法

當把問題分紅了若干步驟並遞歸求解時,若是當前步驟沒有合理的選擇時,則函數將返回上一級遞歸調用,這種現象叫回溯。正是由於這個緣由,遞歸枚舉算法經常被稱爲回溯法 數組

源碼
#include<iostream>
#include<stdio.h>
using namespace std;

int n = 8; 
int c[8]={0};
int count = 0;
void search(int cur);

int main(){
    search(0);
} 

void print(){
    printf("\n-----------------\n");
    for(int i = 0; i < 8; i++){
        for(int j = 0; j < 8; j++){
            if(c[i] == j){
                printf("|*");
            }else{
                printf("| ");
            }
        }
        printf("|\n-----------------\n");
    }
}

void search(int cur){
    if(cur == n){
        print();
        printf("\n");
    }else{
        for(int i = 0; i < n; i++){
            c[cur] = i;
            int ok = 1; 
            for(int j = 0; j < cur; j++){
                if(c[cur] == c[j] || j - c[j] == cur - c[cur] || j + c[j] == cur + c[cur]){
                    ok=0;
                    break;
                }
            }
            if(ok){
                search(cur+1);
            }
        }
    }
}
算法改進

上述算法的枚舉的結點數適合很難減小了,可是程序的效率能夠繼續提升,利用二維數組直接判斷當前嘗試的皇后所在的列和兩個對角線是否已有其餘的皇后,注意的問題是,主對角線的表示y-x可能爲負值,存取時要加上n。函數

void searchs(int cur){
    if(cur == n){
        count++;
    }else{
        for(int i = 0; i < n; i++){
            if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]){
                c[cur]=i;
                vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1;
                searchs(cur + 1);
                vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0;
            }
        }
    }
}

上述程序有個極其關鍵的地方:vis數組的使用。它表示的含義是已經放置的換後佔據了哪些列,主對角線和副對角線。通常的,若是在回溯法中修改了輔助全局變量,則通常要把它們及時恢復原狀,有多個地方修改,每一個地方都要恢復原有的值。
有興趣的話,能夠研究一下n皇后問題,看一看有沒有什麼規律或者快速解法呢?性能

相關文章
相關標籤/搜索