N皇后求解。萬萬沒想到,只用一個一維數組就搞定了。還體現了回溯。

1、啥是N皇后?先從四皇后入手

給定一個4x4的棋盤,要在棋盤上放置4個皇后。他們的位置有這樣的要求,每一列,每一行,每一對角線都能有一個皇后。算法

你可能會對這個對角線有疑惑,其實就是每個小正方形的對角線都不能有皇后。能夠看圖理解一下。
image數組


2、解題思想

設皇后k擺放在x[k]的位置上,注意數組下標從0開始,0<=k<n且0<=x[k]<n。數據結構

這裏用數組下標以及對應的值,模擬了一個棋盤的行和列。這是比較奇妙的地方,不須要二維數組了。code

算法:setQueen(n)
輸入:皇后的個數n
輸出:n皇后問題的解x[n]。解是一個數組。blog

  1. 初始化 k=0 初始化解向量 x[n] ={-1}
  2. 重複執行下面操做,擺放皇后k
    2.1. 把皇后k擺放在下一列的位置,即 x[k]++
    2.2. 若是皇后k擺放在x[k]位置發生衝突,則 x[k]++ 試探下一列,直到不衝突或 x[k] 出界
    2.3. 若是 x[k] 沒出界而且全部皇后都擺放完畢,則輸出一個解
    2.4. 若是 x[k] 沒出界但有皇后還沒有擺放,則 k++ ,轉2.1擺放下一行的皇后
    2.5. 若是 x[k] 出界,則回溯,x[k]=-1 , k-- ,轉2.1從新擺放上一行皇后

3、代碼實現

#include <stdio.h>
#include <cstring>
#include <math.h>
class Queen
{
private:
    int Place(int k);
    int *x;
    int num;

public:
    Queen(int n);
    void setQueen();
    void PrintQueen();
    ~Queen();
};

Queen::Queen(int n)
{
    x = new int[n];
    memset(x, -1, n); //-1表示還沒有擺放皇后
    num = n;
}

Queen::~Queen()
{
    delete[] x;
}

void Queen::setQueen()
{
    int k = 0, count = 0;
    while (k >= 0) //擺放皇后k,注意0<=k<n
    {
        x[k]++;                             //在下一列擺放皇后k
        while (x[k] < num && Place(k) == 1) //發生衝突
            x[k]++;                         //皇后k試探下一列,超出num將會跳出
        if (x[k] < num && k == num - 1)     //獲得一個解
        {
            printf("第%d個解:", ++count);
            PrintQueen();
        }
        else if (x[k] < num && k < num - 1) //尚有皇后未擺放
            k = k + 1;                      //準備擺放下一個皇后
        else
            x[k--] = -1; //重置x[k],回溯,從新擺放皇后k
    }
}

//放置皇后。在一個位置上放置皇后,而後將結果返回。
int Queen::Place(int k) //考察皇后k放置在x[k]列是否發生衝突
{
    for (int i = 0; i < k; i++)
        if (x[i] == x[k] || abs(i - k) == abs(x[i] - x[k])) //根據對角線原則
            return 1;                                       //衝突返回1
    return 0;                                               //不衝突返回0
}

//打印皇后的解
void Queen::PrintQueen()
{
    for (int i = 0; i < num; i++)
        printf("%d\t", x[i] + 1);
    printf("\n");
}

int main(void)
{
    int n;
    printf("請輸入皇后個數(n>=4):");
    scanf("%d", &n);
    Queen Q(n);
    Q.setQueen();
    return 0;
}

4、總結

這裏面的代碼是來自「數據結構C++王紅梅版」string

也知道了不少新穎的點io

  1. 代碼中就很好利用了數組下標做爲形象上理解的一行
  2. 如何斷定兩個皇后是否是在同一對角線。也很好地利用了正方形的特性,皇后所在位置的行差與列差是否相等。
  3. 還有就是SetQueen()的邏輯結構,先用while循環擺放皇后,而後根據這個結果來判斷是已經求解到了,仍是該下一行或者回溯到上一行。

這裏放一波我以前邏輯結構很混亂的代碼(就能夠求解到正確答案,可是輸出的時機很難控制)class

#include <stdio.h>
#include <string.h>
#include <math.h>
#define n 4 //設定n皇后的數目
int *x = new int[n];

void printA();
int place(int k);
int main(void)
{

    memset(x, -1, sizeof(int) * n);
    int k = 0;
    while (k > -1)
    {
        x[k]++;
        if (k < n && place(k) == 1) //不衝突,開始放置下一行   若是已是最後一行呢?
        {
            if (k < n - 1)
                k++;
        }
        else if (k < n || x[k] == n) //要回溯到上一行
        {
            x[k] = -1;
            k--;
        }
        else if (k == n && x[k] < n)
        {
            //獲得一組解答
            printA();
        }
    }

    return 0;
}

//在第k行放置皇后,返回1表明衝突,返回0表明不衝突
int place(int k)
{
    for (; x[k] < n; x[k]++)
    {
        bool flag = true;
        for (int i = 0; i < k; i++)
        {
            if (x[k] == x[i] || (abs(k - i) == abs(x[k] - x[i])))
            {
                flag = false;
                break;
            }
        }
        if (flag)
            return 1;
    }
    return 0;
}

void printA()
{
    for (int i = 0; i < n; i++)
    {
        printf("%d  ", x[i] + 1);
    }
    printf("\n");
}
相關文章
相關標籤/搜索