使用回溯演算法解決N皇后問題以及間隔排列問題
回溯法(探索與回溯法)是一種選優搜尋法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為“回溯點”。
在程式設計中,最容易出錯的地方,便是迭代和遞迴。那麼回溯便是這兩種方法的結合。按照我的理解,使用回溯法需要心中有結構。回溯這個詞用的很形象,按照人的理解就是往後走,但是計算機演算法中的回溯是一個相對的概念,不向前走便是往回走。最外層是一個迭代迴圈,代表的是上一步,迭代中的程式碼有向前走的遞迴,如果在向前走的過程中,沒有滿足相應的約束函式,那麼便繼續上一步的迭代過程,這便稱為回溯。下面我們結合例項來說一下演算法的核心思想,這是我自己寫的程式碼,所以有些地方可能質量不是很高。
在下面的程式碼中,我都把約束函式定義為Place,回溯函式的名稱可以隨便定義。注意觀察回溯函式的結構,大體都一致。
n皇后問題:
在n×n格的棋盤上放置彼此不受攻擊的n個皇后。按照國際象棋的規則,皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。n後問題等價於再n×n的棋盤上放置n個皇后,任何2個皇后不妨在同一行或同一列或同一斜線上。
//n-Quene
bool Place(int k,int i,int* x){
for(int j=0;j<k;j++){//decide wether the current queen conflict with other before queens
if(x[j] == i || abs(x[j]-i)==abs(j-k))
return false;
return true;
}
}
void NQueens(int k,int n,int* x){
for(int i=0;i<n;i++){
if(Place(k,i,x)){
x[k] = i;
if(k == n-1){
for(int i=0;i<n;i++) cout<<i<<" ";
cout<<endl;
}else
NQueens(k+1,n,x);
}
}
}
void NQueens(int n,int* x){
NQueens(0,n,x);
}
int main(){
int n;
do{
cout<<"請輸入n的大小:(n不小於3)"<<endl;
cin>>n;
}while(n<3);
int* x = (int*)malloc(sizeof(int)*n);
NQueens(n,x);
}
約束函式的作用是不讓第k個皇后和以前的皇后發生衝突,那麼這裡的約束函式很好寫啦。注意回溯函式。最外層的for迴圈是將這個皇后在所有可能的列上進行try,如果可以放那麼就通過遞迴向前走。一旦退出遞迴,並且沒有達到n-1,那麼就說面前面有個地方走不通了,繼續for迴圈,這樣便很形象的展示出了什麼是回溯。
下面再來看一個例項問題:
給定n個數,從1-n,每個數取兩個行成2n個數。然後將這2n個數進行排列,使得1之間有1個數,....n之間有n個數。列出所有可能的排序結果。
這道題目可以使用上述類似的方法進行解答。
#include <stdio.h>
#include <iostream>
using namespace std;
//constraint function
bool Place(int k,int i,int* x){//to see whether the selected index i is valid
if(x[i] != 0 || x[i+k+1] != 0)
return false;
return true;
}
void refresh(int k,int n,int* x){
for(int j=0;j<2*n;j++){
if(x[j] <= k)
x[j] = 0;
}
}
//backtrack function
void Backtrack(int k,int n,int* x){
for(int i=0;i<2*n-k-1;i++){
refresh(k,n,x);
if(Place(k,i,x)){
x[i] = x[i+k+1] = k;
if(k == 1){//get the answer state
cout<<"============================"<<endl;
for(int j=0;j<2*n;j++){
if(j == 2*n-1)
cout<<x[j]<<endl;
else
cout<<x[j]<<"\t";
}
}else{
Backtrack(k-1,n,x);
}
}
}
}
int main(){
int n;
do{
cout<<"請輸入n的大小:(n不小於3)"<<endl;
cin>>n;
}while(n<3);
cout<<"排序結果如下所示:"<<endl;
int* x = (int*)malloc(sizeof(int)*n*2);//define the array
//int* x = new int[n];
for(int i=0;i<n*2;i++)//initialize the array
x[i] = 0;
Backtrack(n,n,x);
char a;
cin>>a;
return 0;
}
這裡我的約束函式寫的比較簡單,但是在for迴圈之下,必須加上refresh函式對陣列的狀態進行恢復,否則演算法無法進行,因為我的約束函式是根據陣列的狀態進行判定的。
回溯法在很多情況下都要用到,所以需要好好理解並且靈活的運用。
相關文章
- 回溯法(排列樹)解決八(N)皇后問題
- 用棧+回溯+非遞迴解決N皇后問題遞迴
- N皇后問題
- 回溯法解決全排列問題總結
- 從八皇后問題到回溯演算法演算法
- 國際象棋“皇后”問題的回溯演算法演算法
- js使用遞迴回溯法解八皇后問題程式碼分享JS遞迴
- leetcode演算法題解(Java版)-9-N皇后問題LeetCode演算法Java
- 回溯問題Python框架總結——排列組合問題Python框架
- N皇后問題(各種優化)優化
- 【力扣】排列問題(回溯法)(去重)力扣
- 【演算法】8皇后問題演算法
- 回溯法解決迷宮問題
- 回溯法解決喝酒問題 (轉)
- 資料結構和演算法——遞迴-八皇后問題(回溯演算法)資料結構演算法遞迴
- 遞迴解決全排列問題遞迴
- 回溯問題
- 經典n皇后問題java程式碼實現Java
- 回溯演算法 | 追憶那些年曾難倒我們的八皇后問題演算法
- 解決字串連線設定間隔符的問題字串
- hibernate 1 + N 問題解決
- [演算法]全排列類問題演算法
- 八皇后之回溯法解決
- 演算法學習回顧-皇后問題演算法
- 每日一題之拉低通過率 回溯演算法 leetcode 51 N皇后每日一題演算法LeetCode
- 【問題解決】使用YYYY-MM-dd時間轉換問題
- 【LeetCode回溯演算法#07】子集問題I+II,鞏固解題模板並詳解回溯演算法中的去重問題LeetCode演算法
- 解決「問題」,不要解決問題
- 洛谷八皇后問題
- (回溯法)解決一系列組合問題
- C#資料結構與演算法系列(十四):遞迴——八皇后問題(回溯演算法)C#資料結構演算法遞迴
- 質數填表問題的回溯演算法演算法
- leetcode.回溯演算法能解決什麼問題?LeetCode演算法
- 每天刷個演算法題20160519:回溯法解八皇后演算法
- js解八皇后問題程式碼例項JS
- 機器學習,詳解SVM軟間隔與對偶問題機器學習
- 八皇后問題python解法Python
- 八皇后問題自我總結