這是一道考查應聘者綜合能力的問題,其中包含了演算法的設計、ui的設計、介面的設計等問題,當然在具體面試中沒有時間讓應聘者完成整個設計和編碼,但是面試官往往通過了解應聘者的設計思路和工作步驟來考查應聘者的能力。本節將具體分析這道程式設計題。
所涉及到的知識點
• 回溯演算法
• .net桌面應用程式的ui設計
分析問題
1.整體設計
八皇后問題是乙個非常著名的問題,最初是由著名數學家高斯提出的。問題的描述是這樣的:在乙個88的棋盤上,擺放8個皇后,任意兩個皇后不能處在同一行、同一列和同一斜線上。該問題也可以被擴充套件為在乙個nn的棋盤上擺放n個皇后的問題。而在本節的面試題中,不僅要求尋找解決八皇后問題,並且需要在視窗程式中演示。
應聘者最先要做的不是思考八皇后問題的演算法,而是整體的系統架構,為了簡單起見,這樣的系統可以分成兩個模組,第乙個模組是乙個library型別的專案queensdll,負責模擬問題、提供演算法;而另外乙個模組則致力於視窗演示,是乙個窗體應用程式queensui。這兩個模組之間包括下列介面:
• queensdll接受乙個引數,該引數表示棋盤的維度和皇后的數量,這樣做是為了模擬n皇后的問題。
• queensdll提供乙個公共方法,返回乙個整數說明解的數量。
• queensdll提供乙個公共方法,返回乙個整型陣列來代表乙個解,該方法接受乙個下標引數。
• queensdll提供乙個公共唯讀屬性,返回維度n。
2.使用回溯演算法解決八皇后問題
下面是設計皇后問題的演算法,這裡使用的是較為經典的回溯演算法,該演算法的思路如下:
• 依次在棋盤的每一行上擺放乙個皇后。
• 每次擺放都要檢查當前擺放是否可行。如果當前的擺放引發衝突,則把當前皇后擺放到當前行的下一列上,並重新檢查衝突。
• 如果所有皇后都被擺放成功,則表明成功找到乙個解,記錄下該解並且回溯到上乙個皇后。
• 如果第乙個皇后也被回溯,則表明已經完成所有可能性的計算。
在開始實際編寫演算法前,還需要考慮棋盤和棋子擺放的模擬方式,這裡採取簡單的陣列方法,使用乙個int[n]的陣列來模擬乙個棋盤,而該陣列某個下標的值,則代表了該行擺放的列數。
int data=new int[3];
(1)有了棋盤的表達方式,根據整個系統的設計,可以初步寫下核心模組的結構,如**13-20所示。
**13-20 八皇后問題:queens.cs
///
/// 皇后問題核心模組
///
public partial class queens
///
/// 構造方法,需要提供維度來構造
///
/// 維度
public queens(int n)
///
/// 唯讀屬性,返回維度
///
public int dimension
}public int getchessboard(int index)
///
/// 得到解的數量
///
public int getcount()
}(2)上述**定義了所有對外公開的公共方法和屬性,並且定義了內部成員和構造方法。在此基礎上,就可以實現回溯演算法了,如**13-21所示。
**13-21 八皇后問題:queens.cs
///
/// 回溯演算法的實現
///
public partial class queens
//不需要回溯
else
found = true;
break;}}
if (!found)
else
j++;}}
///
/// 檢查當前擺放是否有衝突
///
/// 現在擺放第幾個皇后
/// 當前棋盤
/// 當前嘗試擺放的列數
///
private bool noconflict(int index, int board, int val)
//檢查接觸,無衝突
return true;}}
演算法的注釋已經詳細解釋了演算法的思路,這裡有幾點讀者在編寫回溯演算法時候需要特別注意:
• 回溯迴圈的結束在於第乙個皇后被回溯。
• 當找到乙個解時,需要複製整個棋盤,不然接下來的回溯將破壞已經找到的解。
• 找到乙個解後,需要在當前皇后的基礎上回溯。
• 回溯乙個皇后時,要對當前的列數進行重置。
一般在編寫完核心**後,需要編寫一定的測試**進行單元測試,而不是馬上在ui模組中應用演算法。這裡為了突出重點,筆者省略了測試**。但筆者強烈建議讀者在實際工作時為每個模組編寫測試**。
3.編寫ui窗體來演示皇后問題
• 使用者需要輸入維度,即皇后問題中的n。
• 需要向使用者輸出n皇后問題的解的數量。
• 需要提供介面,允許使用者遍歷每個解。
• 對於每個解,需要使用圖示的方式繪畫整個棋盤。
(1)初步設計整個ui介面
(2)編寫後台**來具體實現ui的功能,首先是「計算」按鈕的**,這裡需要讀入使用者輸入的維度,並且呼叫queensdll模組來生成具體的解,如**13-22所示。
**13-22 八皇后問題:queensui.cs
///
/// ui後台**,接受輸入,計算所有的解
///
public partial class queensui : form
//轉換失敗,輸入的不是乙個整數
int num;
if (!int32.tryparse(numstring, out num))
//輸入的整數小於0
if (num <= 0)
//輸出話所有解
_queens = new queens(num);
//計算棋盤格仔的寬度
_width = (panel.width - 20) / _queens.dimension;
//顯示解的數量
total.text = "一共有:" + _queens.getcount().tostring() + "種方式";
//繪畫棋盤和解
display(0);}}
**13-23 八皇后問題:queensui.cs
///
/// 遍歷所有的解
///
public partial class queensui : form
///
/// 單擊上乙個按鈕事件
///
private void former_click(object sender, eventargs e)
}(4)結束這些按鈕響應**的編寫後,就是ui模組最核心的部分:繪畫某乙個解。繪畫的步驟包括繪畫整個棋盤和每個皇后的位置。棋盤以交錯的橫線表示,而皇后的位置則用乙個實心的圓來表示。
**13-24 八皇后問題:queensui.cs
///
/// 繪畫乙個解
///
public partial class queensui : form
//重新整理畫板,擦除上一次繪畫
panel.refresh();
//得到需要繪畫的解
int board = _queens.getchessboard(index);
//生成畫圖元件
using (graphics g = panel.creategraphics())
}//設定當前解的下標
_current = index;
//輸出給使用者
current.text="當前是第"+(_current+1).tostring()+"種";
}///
/// 繪畫乙個棋盤
///
/// 繪畫資源
/// 畫筆
private void drawboard(graphics dc, pen pen)
}///
/// 繪畫皇后的位置
///
/// 儲存位置的陣列
/// 繪畫資源
/// 畫筆
private void drawqueen(int queens,graphics dc,brush brush)}}
(5)至此整個系統就完成了,嘗試編譯並執行該程式,輸入8來演示八皇后問題,將得到如圖13.6所示的輸出效果。
圖13.6 八皇后問題的執行結果
在實際面試和筆試中,一般不會讓應聘者完成整個系統的編碼,但應聘者需要對系統的設計過程和演算法的思想有比較深刻的理解。
答案八皇后乃至n皇后的問題,通常使用回溯演算法來解決。而類似於本節問題的面試題,不僅考查了應聘者的演算法能力,也考查了應聘者的系統設計能力。
回溯 皇后 演算法筆記 回溯演算法 N皇后問題
n 皇后問題研究的是如何將 n 個皇后放置在 n n 的棋盤上,並且使皇后彼此之間不能相互攻擊。上圖為 8 皇后問題的一種解法。給定乙個整數 n,返回所有不同的 n 皇后問題的解決方案。每一種解法包含乙個明確的 n 皇后問題的棋子放置方案,該方案中 q 和 分別代表了皇后和空位。示例 輸入 4 輸出...
n皇后問題(回溯演算法)
問題定義 西洋棋中,皇后可以直線進攻也可以斜線進攻,問在nxn的西洋棋棋盤上擺n個皇后,問擺使得n個皇后之間無法相互進攻有多少種擺法?通過回溯法解決。def isvalid matrix,row,col n len matrix for i in range row for j in range n...
求 N 皇后問題回溯演算法
file queen.c description 求 n 皇后問題回溯演算法 include define delaytime 20000 顯示棋局時間 define topx 10 棋盤左上角 x 座標 define topy 5 棋盤左上角 y 座標 int n 皇后數量 int a 8 b 1...