C++代碼(2)八皇后問題
八皇后實在太有名了,我也就不廢話了。利用回溯算法,不難寫出其代碼,相信各位同學也都干過了。那這篇文章還有何新意呢?難道是向各位展示在下的代碼要比各位好,絕對不是。只因用C++寫代碼的時候,很容易就陷入各種細節的糾纏中,必須牢記大刀闊斧地砍除無關緊要的細節,始終堅持用C++清晰地表達解決問題的思路,嚴格遵守單一職責的規則,集中精力解決問題,不賣弄半點花巧。這是在下努力的一種嘗試。八皇后的解決方案,相信大家也都知道了,請恕我直奔主題了。
由于長年累月沉浸于C++中,中毒已經甚深了,碰到任何問題,都條件反射地將其大御八塊,然后用CLASS來組織。于是,非常自然的,我決定用類QueenGame來表示八皇后這個游戲,用Play來擺弄出一個安全棋局。當然,不用CLASS,也依然可以寫出清晰的代碼,但是,感覺總是很不習慣。于是,main起來就非常簡單了。
int main()
{
QueenGame game(8);
if(game.Play())
DisplayQueen(game);
return 0;
}
接下來就要思考QueenGame里面有那些東東,除了Play,肯定還有一些其他東東,最起碼也應該有些房子來供皇后們居住。最自然的想法是用一個二維的bool數組chessboard來表示棋盤,假如chessboard[i][j]為true,就表示皇后在上面。但是,目前市面上的作法是用數組來儲存N個皇后的位置,數組上的元素的索引表示皇后在棋盤上第幾行,其值對應皇后在第幾列上,這種儲存方式相當方便高效,二維數組如何搗鼓,都沒它方便。因此,可以這樣定義后宮的地點int m_QueenPos[N],代碼有點呆板,似乎應該用使用指針或者引入vector,來靈活處理不同數量的N個皇后,但這樣做,將引入一些其他的復雜性,對于本道程序,大可以假設皇后不會太多,10個已經足夠多了,犧牲一點點空間,來踢開這個細節,換取程序的安全穩定,我非常樂意。
由于QueenGame可以Play不同數目的皇后,從1個到10個都可以,因此在QueenGame的構造函應該確立皇后的數量,同時,再提供一個函數GetQueenCount,用以獲取她們的數目。QueenGame的初版如下:
class QueenGame
{
public:
enum {MAX_QUEEN_COUNT = 10};
QueenGame(int queenCount);
public:
int GetQueenCount()const;
bool Play();
private:
int m_QueenPos[MAX_QUEEN_COUNT];
};
void DisplayQueen(const QueenGame& game)
{
using namespace std;
int count = game.GetQueenCount();
for (int n = 0; n<count; n++)
{
for (int i=1; i<=count; i++)
{
if (game.m_QueenPos[n] == i)
cout << "Q";
else
cout << "+";
}
cout << endl;
}
cout << endl;
}
bool QueenGame::Play()
{
int& lastPos = m_QueenPos[m_nLast];
while (++lastPos <= m_nQueenCount)
{
if (testNewQueen())
break;
}
if (lastPos > m_nQueenCount)
{
m_nLast--;
if (m_nLast<=0 && m_QueenPos[0] > m_nQueenCount)
return false;
}
else
{
if (m_nLast+1 == m_nQueenCount)
return true;
m_nLast++;
m_QueenPos[m_nLast] = 0;
}
return Play();
}
class QueenGame
{
public:
enum {MAX_QUEEN_COUNT = 10};
QueenGame(int queenCount)
{
assert(queenCount > 0 && queenCount <= MAX_QUEEN_COUNT );
m_nQueenCount = queenCount;
m_nLast = 0;
m_QueenPos[m_nLast] = 0;
}
public:
int GetQueenCount()const
{
return m_nQueenCount;
}
bool Play();
int m_QueenPos[MAX_QUEEN_COUNT];
private:
bool testNewQueen();
int m_nQueenCount;
int m_nLast;
};
bool QueenGame::testNewQueen()
{
int lastPos = m_QueenPos[m_nLast];
for (int i=0; i<m_nLast; i++)
{
if (m_QueenPos[i] == lastPos || abs(m_QueenPos[i]-lastPos)==(m_nLast-i))
return false;
}
return true;
}
顯然,這里采用了深度優先的搜索算法,代碼也可以寫成不用遞歸的形式,還有,這里也可以用寬度優先搜索法,這一切就有待各位嘗試了。
又,程序采用了一點點匈牙利的命名習慣,這是MFC用久了所沾染上的惡習,沒辦法改了,偶也知道匈牙利命名的種種弊端。



