1. 程式人生 > >八皇后問題(遞迴,回溯)

八皇后問題(遞迴,回溯)

八皇后問題是一個以國際象棋為背景的問題:如何能夠在
8×8 的國際象棋棋盤上放置八個皇后,使得任何一個皇后都無法直接吃掉其他的皇后?為了達到此目的,任兩個皇后都不能處於同一條橫行、縱行或斜線上。八皇后問題可以推廣為更一般的n皇后擺放問題:這時棋盤的大小變為n×n,而皇后個數也變成n。當且僅當 n = 1 或 n ≥ 4 時問題有解。

因此只需要判斷放下的當前位置的棋子的位置是否被之前放下的棋子位置有衝突即可。可以使用普通的二維陣列的判讀,但是這樣寫起來很麻煩,有冗餘。

此題
可以藉助兩個二維陣列圖來幫助理解,分別建立兩個x,y-x 的二維陣列和x,x+y的二維陣列來進行理解。cur-c[cur]==j-c[j] || cur+c[cur]==j+c[j] 用來判斷皇后(cur,c[cur])和(j,c[j])是否在同一條對角線上。使用c[cur]==c[j]判斷是否在同一列上。
這裡寫圖片描述

程式碼為:

#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;
int n,tot;
int c[50];
void dfs(int cur)
{
    if(cur==n)
        tot++;
    else for(int i=0;i<n;i++)
    {
        c[cur]=i;  //把第cur行的皇后放在第i列
        int ok=1;
        //檢查是否和前面的皇后衝突
        for(int
j=0;j<cur;j++) { if(c[cur]==c[j]||cur-c[cur]==j-c[j]||cur+c[cur]==j+c[j]) { ok=0; break; } } if(ok) dfs(cur+1); } } int main() { while(cin>>n&&n) { memset(c,0,sizeof
(c)); tot=0; dfs(0); cout<<tot<<endl; } }

結點數很難進一步減少,但程式效率可以繼續提高:利用二維陣列vis[2][]直接判斷當前嘗試的皇后所在的列和兩個對角線是否有其他皇后。注意對角線標y-x可能為負,存取時要加上n。

因此程式碼可以改為:

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
int n,c[50],tot,vis[3][50];
void search(int cur)
{
    if(cur==n)
        tot++;
    else for(int i=0;i<n;i++)
    {
        //利用二維陣列直接判斷
        if(!vis[0][i]&&!vis[1][cur-i+n]&&!vis[2][cur+i])
        {
            c[cur]=i;  //用來列印,如果不用列印結果,可以刪除
            vis[0][i]=vis[1][cur-i+n]=vis[2][cur+i]=1;
            search(cur+1);
            vis[0][i]=vis[1][cur-i+n]=vis[2][cur+i]=0;
        }
    }
}
int main()
{
    while(cin>>n&&n)
    {
        tot=0;
        memset(vis,0,sizeof(vis));
        search(0);
        cout<<tot<<endl;
    }
}