輸入一個N,找出所有在N行N列的棋盤擺放N個皇后的方法。要找出所有的解,是一個經典的使用回溯法的例子。都在注釋里了:
public class NQueen { public static void main(String[] args) { Scanner sc = new Scanner(System.in); while(sc.hasNext()) { int N = sc.nextInt(); //假設輸入的N都是合法的,這里省略了一些檢查條件 int []a = new int[N]; //a[i]表示第i行擺放的列數 printQueenR(0,N,a); //遞歸算法,從第0行開始找放置位置 //printQueen(a,N); //非遞歸就用這行 } return ; } // (遞歸) 這個方法是確定第 k 行的皇后的列的位置,將列數賦給 a[k] public static void printQueenR(int k,int N,int []a){ if(N == k) { //如果當前行已經超過最大行了,說明某一種放置方法已經好了,輸出 for(int i = 0;i < N;i++) { System.out.print(a[i] + 1 + " "); } System.out.print("\n"); return ; } for(int i = 0; i < N;i++) { //第k行的第i列 a[k] = i; //先將k行的列數置為i if(legal(k,a)) //k行的列數為i時,是否和0--(k-1)行沖突了,沒有則遞歸調用,繼續尋找k+1行的位置 printQueenR(k+1,N,a); //就算進入遞歸,結束后也繼續循環,i++,再嘗試k行的另一個列位置,嘗試找出另一種解 } } //(非遞歸) public static void printQueen(int a[],int N) { int t = 0; a[0] = -1; //初始條件,第0行的默認列置為-1 while(t >= 0) { //t代表行數 ,a[t]代表第t行第a[t]列 a[t]++; //每循環一次,則嘗試本行的下一列 while(a[t] < N && !legal(t,a)) { //在本t行順序找到一個不沖突的列的位置 a[t]++; } if(a[t] < N) { //如果這個列合法,即不超出棋盤 if (t == N - 1) { //且目前的t行已經到頭了,則輸出本次放置方案 for (int i = 0; i < N; i++) { System.out.print(a[i] + 1 + " "); } System.out.print("\n"); } else //沒到頭就行數+1,准備開始下一行。由於下一行一個列都沒試過,並把下一行的初始列置為-1 a[++t] = -1; } else { //如果列已經試到超出棋盤了,說明t行的所有列都試過了還沒有找到合適的位置,則返回下一行找另一種方案。 t--; //當t<0,時執行這句話說明第0行的所有列都嘗試完畢,直接退出循環
} } } //公共方法。剪枝條件:檢查第k行的放置的列是否合法,即是否與0--(k-1)行的放置沖突 public static boolean legal(int k,int a[]) { for (int i = 0; i < k; i++) {
//與其他行的擺放成一列,或成斜線就不合法 if(a[k] == a[i] || abs(k - i) == abs(a[i]-a[k])) { return false; } } return true; } }
這里運用了一個剪枝條件大大減少了復雜度。若是蠻力破解的話復雜度是:O(N^N),因為每一行都要試N次,一共N行。剪枝后的復雜度挺難計算的,最壞是O(N!),一般是指數級的樣子