1. 程式人生 > >演算法-N皇后問題

演算法-N皇后問題

2018/9/23

N皇后問題:在NxN的棋盤上,放置N個棋子, 使得同一行、同一列、同一對角線上只有一個棋子,問有多少種滿足條件的放置方法?

        回溯法和位運演算法

方法1: 【回溯法】 回溯法也叫試探法,她是一種系統地搜尋問題的解的方法。

               回溯法的基本思想是:從一條路往前走,能進則進,不能進則退回來,換一條路再試。

    回溯法是解決一種問題的“萬能演算法”,這種問題是:需要將所有可能情況都窮舉出來,然後從中找出滿足某種要求的可能情況或最優解,從而得到整個問題的解。

/******

* 非遞迴方法的一個重要問題時何時回溯及如何回溯的問題。

程式首先對N行中的每一行進行探測,尋找該行中可以放置皇后的位置,具體方法是對該行的每一列進行探測,看是否可以放置皇后,

如果可以,則在該列放置一個皇后,然後繼續探測下一行的皇后位置。如果已經探測完所有的列都沒有找到可以放置皇后的列,

此時就應該回溯,把上一行皇后的位置往後移一列,如果上一行皇后移動後也找不到位置,則繼續回溯直至某一行找到皇后的位置或回溯到第一行,

如果第一行皇后也無法找到可以放置皇后的位置,則說明已經找到所有的解程式終止。

如果該行已經是最後一行,則探測完該行後,如果找到放置皇后的位置,則說明找到一個結果,打印出來。

但是此時並不能再此處結束程式,因為我們要找的是所有N皇后問題所有的解,此時應該清除該行的皇后,從當前放置皇后列數的下一列繼續探測。

*

*回溯法解N皇后問題

*使用一個一維陣列表示皇后位置a[N]

*其中陣列的下標表示皇后所在的行row, 陣列元素值表示皇后所在的列

*這樣設計的棋盤,所有皇后必定不在同一行,所以行衝突是不存在的

*********/

#include <stdio.h>

#include <iostream>

#include <math.h>  //判斷同一對角線上是否有衝突

using namespace std;

#define N      8   //皇后的數目

#define INIT   -100 //棋盤的初始值(儘可能小的負數),因為a[i]取值從0~N-1

int a[N];  //用一維陣列表示棋盤

void queue_init()

{

    for (int i = 0; i < N; i++)

    {

        a[i] = INIT;

    }

}

bool judge_queue_valid(int row, int col)

{

    for (int i = 0; i < N; i++)

    {

        if (a[i] == col || abs(row - i) == abs(col - a[i])) //如果列衝突或者對角線衝突,則無效

        {

            return false;

        }

    }

    return true;

}

int queue_core()

{

    int i = 0, j = 0; //i表示row行號,j表示col列號

    int n = 0; //可行的放置方案數

    while (i < N)

    {

        while (j < N) //對i行中的每一列進行探測,看是否可以放置皇后

        {

            if (judge_queue_valid(i, j)) //判斷放置此處是否滿足N皇后條件

            {

                a[i] = j;  //滿足條件在i行j列放置皇后

                j = 0;   //因為下一行又要從第一列開始遍歷判斷,所以列清零

                break;  //退出內迴圈

            }

            else //i行j列這個位置不滿足N皇后條件

            {

                j++; //繼續在此行下一列探測,直到此行最後一個位置探測完自然退出內迴圈

            }

        }

        //i行的所有N列已經探測完了,此處判斷是否找到了滿足條件的位置

        if (INIT == a[i]) //如果此行沒有滿足條件的,則需要回溯,回溯是從後往前依次重新判斷

        {

            if (0 == i) //i==0,回溯到最後依次:說明剛剛的內迴圈是在第一行中尋找滿足條件的解,但是可惜沒有找到

            {

                break; //找完了所有滿足條件的解,退出大迴圈

            }

            else  //不是最開始的一行,則往上回溯

            {

                i--;           //回溯到上一行

                j = a[i] + 1;  //從上一次滿足條件的下一個位置resume judge

                a[i] = INIT;    //把上一行皇后的位置清除,重新探測

                continue;      // 設定好j後,重新開始內迴圈

            }

        }

        //如果a[i]不是初始值,則說明i行a[i]列滿足條件,則繼續往下執行

        if ( N - 1 == i) //i探測的結果是最後一行

        {

            n++; //滿足的方案數目

            j = a[i] + 1;  //從最後一行放置皇后的下一列進行回溯探測

            a[i] = INIT;  //清除最後一行的皇后位置

            continue;

        }

        i++; //如果第i行滿足條件,並且不是最後一行,則繼續探測下一行

    }//end while(i<N)

    return n;

}

int main()

{

    int solution_count = 0;

    queue_init();

    solution_count = queue_core();

    cout << solution_count << endl;

    system("pause");

    return 0;

}

方法1: 【位運演算法】  --未完待續。。。

         位運算基本操作的意義: