經典回溯問題——八皇後問題
阿新 • • 發佈:2019-03-15
條件判斷 out 進行 過程 sum 增加 不能 return 什麽
算法提出:
在國際象棋棋盤上(8*8)放置八個皇後,使得任意兩個皇後之間不能在同一行,同一列,也不能位於同於對角線上。問共有多少種不同的方法,並且指出各種不同的放法。
算法思路:
首先我們分析一下問題的解,我們每取出一個皇後,放入一行,共有八種不同的放法,然後再放第二個皇後,同樣如果不考慮規則,還是有八種放法。於是我們可以用一個八叉樹來描述這個過程。從根節點開始,樹每增加一層,便是多放一個皇後,直到第8層(根節點為0層),最後得到一個完全八叉樹。
緊接著我們開始用深度優先遍歷這個八叉樹,在遍歷的過程中,進行相應的條件的判斷。以便去掉不合規則的子樹。
那麽具體用什麽條件來進行子樹的裁剪呢?
我們先對問題解的結構做一個約定。
用X[i]來表示,在第i行,皇後放在了X[i]這個位置。
於是我們考慮第一個條件,不能再同一行,同一列於是我們得到x[i]不能相同。剩下一個條件是不能位於對角線上,這個條件不是很明顯,我們經過分析得到,設兩個不同的皇後分別在j,k行上,x[j],x[k]分別表示在j,k行的那一列上。那麽不在同一對角線的條件可以寫為abs((j-k))!=abs(x[j]-x[k]),其中abs為求絕對值的函數。
於是下面我們便可以利用一個遞歸的調用來遍歷八叉樹。
我們首先定義一個訪問某節點所有子節點的函數
void backtrack(int t) { if(t>num) //num為皇後的數目 { sum++;//sum為所有的可行的解 for(int m = 1;m<num;m++) { cout<<x[m];//這一行用輸出當遞歸到葉節點的時候,一個可行解 } cout<<endl; } else for(int i = 1;i<=num;i++) { x[t] = i; if(place(t)) backtrack(t+1);//此處的place函數用來進行我們上面所說的條件的判斷,如果成立,進入下一級遞歸 } }
下面我們定義了條件判斷函數
bool place(int k) { for(int j = 1;j<k;j++) if(abs(x[k] - x[j]) == abs(k-j)||x[j] == x[k]) return false; return true; }
上面的函數便是按照我們上面說介紹的條件進行判斷。
最後就是主程序的調用了
static int num; static int *x; static int sum; void main() { num = 8; sum = 0; x = new int[num+1]; for(int i= 0;i<=num;i++) x[i] = 0; backtrack(1); cout<<"方案共有"<<sum; }
經典回溯問題——八皇後問題