八皇后問題三種實現

八皇后問題三種實現

八皇后問題,是一個古老而著名的問題,是回溯算法的典型案例。該問題是國際西洋棋棋手馬克斯·貝瑟爾於1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。 高斯認爲有76種方案。1854年在柏林的象棋雜誌上不同的作者發表了40種不同的解,後來有人用圖論的方法解出92種結果。計算機發明後,有多種計算機語言可以解決此問題。(摘自百度)


這裏用回溯法和遞歸實現了3種。
1 C++ 回溯
2 C 回溯
3 C 遞歸實現
92組解

擺放棋子的規則是:

行從上向下,列從左到右試探。

C++回溯解法

#include <deque>
using namespace std;

static int const N = 8;
//
// 下棋位置
//
struct ChessPoint
{
    ChessPoint(){}
    ChessPoint(int x,int y)
    {
        this->x = x;
        this->y = y;
    }
    ChessPoint & operator = (ChessPoint & cp)
    {
        this->x = cp.x;
        this->y = cp.y;
        return *this;
    }
    //橫座標,列
    int x;
    //縱座標,行
    int y;
};


static bool CanPlace(int x,int y,deque<ChessPoint> &vec)
{
    deque<ChessPoint>::iterator it = vec.begin();
    for(;it!=vec.end();it++)
    {
        if( it->x == x || 
            it->y == y || 
            it->x-x == it->y-y ||
            x-it->x == it->y-y )
        {
            return false;
        }
    }
    return true;    
}
//
// passPath - 經過的路徑
// lineNum - 現在檢查的行
//
bool GetFreePoint(deque<ChessPoint> &result,ChessPoint &retCP)
{
    ChessPoint cp = *result.rbegin();
    int x = 0;
    int y = 0;
    x = cp.x+1;
    y = cp.y;
    result.pop_back();
    //
    // 這一行繼續查
    //
    for(;;)
    {
        if(x>=N)
        {
            if(y<0)
            {
                break;
            }
            cp = *result.rbegin();
            //
            //試探下一列;行不變
            //
            x = cp.x+1;
            y = cp.y;
            if(x>=N)
            {
                //
                // 此行已經試探完畢,回到上一行
                //
                result.pop_back();
                continue;
            }
            result.pop_back();
        }
        if(CanPlace(x,y,result))
        {
            retCP.x=x;
            retCP.y=y;
            return true;
        }
        else
        {
            x+=1;
        }
    }
    return false;
}

void queen()
{
    //
    // x -> (0 , N-1) , x 是列
    // y -> (0 , N-1) ,y 是行
    //
    int x = 0;
    int y = 0;

    //
    // 結果存放在一個vector中,有 N 個元素,每行一個
    //
    deque<ChessPoint> resultVec;

    while(1)
    {
        //
        // 出口
        //
        if(resultVec.size()==N)
        {
            break;
        }
        //
        // 判斷(x,y)此點是否可以
        //
        if(CanPlace(x,y,resultVec))
        {
            //
            // 存入結果
            //
            ChessPoint pp(x,y);
            resultVec.push_back(pp);
            //
            // 行加一
            //
            y+=1;
            //
            // 列歸零
            //
            x=0;
        }
        else
        {
            if(x < N)
            {
                //
                // (x,y) 此點不能匹配,移動到下一列
                //
                x+=1;
                //
                // 沒有列可以選擇了
                //
                if(x>=N)
                {
                    //
                    // 沒有找到匹配的列,則回溯到上一行,找到一個沒有經過的座標
                    //
                    ChessPoint po;
                    if(GetFreePoint(resultVec,po))
                    {
                        x = po.x;
                        y = po.y;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            else
            {
                //
                // 沒有找到匹配的列,則回溯到上一行,找到一個沒有經過的座標
                //
                ChessPoint po;
                if(GetFreePoint(resultVec,po))
                {
                    x = po.x;
                    y = po.y;
                }
                else
                {
                    break;
                }
            }
        }
    }
}

C回溯解法

<span style="font-size:12px;">//
// 使用回溯法解皇后問題
// 當下一行找不到合適的位置,則返回到上一行,試探下一個位置,知道找到合適的位置;
// 當找到一個解以後,放棄這個位置,回溯找下一個合適的位置,直到找不到合適的位子,
// 就將所有解找到了。
//
//
// 要放8個皇后
//
static const int N = 8;

//
// 已經放置的皇后個數
//
static int count = 0;

//
// 解的個數
//
static int solution = 0;

//
// 下棋位置
//
struct ChessPoint
{
    ChessPoint(){}
    ChessPoint(int x,int y)
    {
        this->x = x;
        this->y = y;
    }
    ChessPoint & operator = (ChessPoint & cp)
    {
        this->x = cp.x;
        this->y = cp.y;
        return *this;
    }
    //橫座標,列
    int x;
    //縱座標,行
    int y;
};


//
// canPlace:是否可以放置皇后呢?
// line - 行,col - 列
//
static bool CanPlace(int x,int y,unsigned char* vec)
{
    for(int i=0;i<count;i++)
    {
        if( i == y || 
            vec[i] == x || 
            i-y == vec[i]-x ||
            y-i == vec[i]-x )
        {
            return false;
        }
    }
    return true;    
}
//
// passPath - 經過的路徑
// lineNum - 現在檢查的行
//
static bool GetFreePoint1(unsigned char* result,ChessPoint &retCP)
{
    int x = 0;
    int y = 0;
    x = result[count-1]+1;
    y = count-1;
    count-=1;
    //
    // 這一行繼續查
    //
    for(;;)
    {
        if(x>=N)
        {
            if(y<0)
            {
                break;
            }
            x = result[count-1]+1;
            y = count-1;
            if(x>=N)
            {
                count-=1;
                continue;
            }
            count-=1;
        }
        if(CanPlace(x,y,result))
        {
            retCP.x=x;
            retCP.y=y;
            return true;
        }
        else
        {
            x+=1;
        }
    }
    return false;
}
//
// printResult:打印結果
// resultVec - 結果數組
//
void printResult(unsigned char *resultVec)
{
    printf("-------%d皇后的解 第%d組---------\n",N,++solution);
    for(int i=0;i<N;i++)
    {        
        for(int j=0;j<N;j++)
        {
            if(resultVec[i]==j)
            {
                printf("1 ");                
            }
            else
            {
                printf("0 ");
            }
        }
        printf("\n");
    }

}
//優化實現
void queen1()
{
    //
    // x -> (0 , N-1) , x 是列
    // y -> (0 , N-1) ,y 是行
    //
    int x = 0;
    int y = 0;

    //
    // 結果存放在數組中,數組的索引代表行,值代表對應的列
    //
    unsigned char resultVec[N];

    while(1)
    {
        //
        // 出口
        //
        if(count == N)
        {
            printResult(resultVec);
            //
            // 繼續找下一個解
            //
            ChessPoint po;
            if(GetFreePoint1(resultVec,po))
            {
                //
                // 個數減一,並且回溯
                //
                x = po.x;
                y = po.y;
            }
            else
            {
                //
                //再沒有解了
                //
                break;
            }
        }
        //
        // 判斷(x,y)此點是否可以
        //
        if(CanPlace(x,y,resultVec))
        {
            //
            // 存入結果
            //
            resultVec[y]=x;
            count+=1;
            //
            // 行加一
            //
            y+=1;
            //
            // 列歸零,從第一列試
            //
            x=0;
        }
        else
        {
            if(x < N)
            {
                //
                // (x,y) 此點不能匹配,移動到下一列
                //
                x+=1;
                //
                // 沒有列可以選擇了
                //
                if(x>=N)
                {
                    //
                    // 沒有找到匹配的列,則回溯到上一行,找到一個沒有經過的座標
                    //
                    ChessPoint po;
                    if(GetFreePoint1(resultVec,po))
                    {
                        x = po.x;
                        y = po.y;
                    }
                    else
                    {
                        //
                        // 找不到合適的座標了
                        //
                        break;
                    }
                }
            }
            else
            {
                //
                // 沒有找到匹配的列,則回溯到上一行,找到一個沒有經過的座標
                //
                ChessPoint po;
                if(GetFreePoint1(resultVec,po))
                {
                    x = po.x;
                    y = po.y;
                }
                else
                {
                    //
                    // 找不到合適的座標了
                    //
                    break;
                }
            }
        }
    }
}
</span>

C 遞歸解法

<span style="font-size:10px;">//
// 這是教課書上的解法,遞歸解決。
// queen3 以每行作爲一個單位
//
//
// 解的個數,最後8皇后得到92組解
//
static int solution = 0;
//
// 放幾個皇后?
//
static const int N=8;
//
// 已經放置的個數,如果爲8個,則找到一組解
//
static int count = 0;
//
// 這裏存放一組解
//
static int result[N];
//
// canPlace:是否可以放置皇后呢?
// line - 行,col - 列
//
static bool canPlace(int line,int col)
{
    for(int i=0;i<count;++i)
    {
        if( i == line               ||
            col == result[i]     || 
            i-line==col-result[i]||
            line-i == col-result[i])
        {
            return false;
        }
    }
    return true;
}
void printResult()
{
    for(int i=0;i<N;++i)
    {
        for(int j=0;j<N;++j)
        {
            if(j == result[i])
            {
                printf("1 ");
            }
            else
            {
                printf("0 ");
            }
        }
        printf("\n");
    }
    printf("\n");
}

//
// queen3 
// line - 從哪行開始?
//
bool queen3(int line)
{
    bool bFound = false;
    //
    // 當填滿的時候打印
    //
    if(count == N)
    {
        printResult();
        ++solution;
        printf("  %d \n",solution);
        //count -= 1;
    }
    //
    // 當到了最後一行的時候,運行到這裏說明已經找到了一條可行的路徑;
    // 而繼續查找就要回退一行,然後繼續下一列,因此返回false,回退一行
    //
    if(line==N)
    {
        return false;
    }
    //
    //i 代表列
    //    
    for(int i=0;i<N;i++)
    {
        if(canPlace(line,i))
        {
            result[line]=i;
            count+=1;
            if(line<N)
            {
                if(!queen3(line+1))
                {
                    count-=1;
                }
                else
                {
                    bFound = true;
                }
            }
        }
    }
    //
    // 如果沒有合適的位置,則返回false,然後就會回退一行,繼續試探下一個位置
    //
    if(!bFound)
    {        
        return false;
    }
    return true;
}</span>

//主函數調用:
void queen();
void queen1();
bool queen3(int line);
int _tmain(int argc, _TCHAR* argv[])
{
    queen1();
    queen();
    queen3(0);
}

每一種解法是一個文件,主函數是一個文件。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章