1. 程式人生 > >演算法之n皇后問題

演算法之n皇后問題

1. 問題描述:n×n的棋盤上放置彼此不受攻擊的n個皇后。按照國際象棋的規則,皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。n皇后問題等價於在n×n的棋盤上放置n個皇后,任何2個皇后不能放在同一行或同一列或同一斜線上。

2. 問題分析:

n元陣列x[n]表示問題的解。其中x[i]表示皇后i放在棋盤第i行第x[i]列。由於不允許將2個皇后放在同一列上,所以解向量中的x[i]互不相同,

2個皇后不能放在同一斜線上是問題的隱約束。設2個皇后的放置位置分別是(i,j)(k,l),則如果滿足|i-k|=|j-l|,就表明2個皇后位於同一條斜線上。

分析可知,解空間樹是完全

n叉樹,剪枝函式剪去不滿足行、列、斜線約束的子樹。

#include <iostream>
#include <cmath>
using namespace std;

int N=5;
int x[5]; //可行解
int sum=0;//可行解數量

bool Place(int k)
{
    for(int i=0; i<k; i++)
    {
        if(abs(k-i)==abs(x[i]-x[k])||x[i]==x[k])return false;
    }
    return true;
}
void Backtrack(int t)    //遞歸回溯
{
    if(t>=N)
    {
        sum++;//到達葉子結點,即找到了一個新的可行解,則解的數量sum++;
        for(int i=0; i<N; i++)
        {
            cout<<x[i]<<" ";
        }
        cout<<endl;
    }
    else
    {
        for(int i=0; i<N; i++)
        {
            x[t]=i;
            if(Place(t))Backtrack(t+1);
        }
    }
}

void Backtrack2()    //迭代回溯
{
    x[0]=0;
    int k=0;
    while(k>=0)
    {
        while(x[k]<N&&Place(k)==false)x[k]+=1;
        if(x[k]<N)
        {
            if(k==N-1)      //到了葉子結點,輸出一個解,回溯
            {
                sum++;
                for(int i=0; i<N; i++)
                {
                    cout<<x[i]<<" ";
                }
                cout<<endl;
                k--;
                x[k]+=1;
            }
            else    //沒到葉子結點繼續往下走
            {
                k++;
                x[k]=0;
            }
        }
        else    //說明當前行無法放置,回溯
        {
            k--;
            x[k]+=1;
        }
    }
}
int main()
{
    //Backtrack(0);
    Backtrack2();

    /*0 2 4 1 3
    0 3 1 4 2
    1 3 0 2 4
    1 4 2 0 3
    2 0 3 1 4
    2 4 1 3 0
    3 0 2 4 1
    3 1 4 2 0
    4 1 3 0 2
    4 2 0 3 1*/
}

3.時間複雜度:最壞時間複雜度Place方法O(n),所以Backtrack方法時間複雜度為O(n^3);