使用回溯演算法解決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皇后問題--回溯法,以DFS的方式搜尋
- leetcode演算法題解(Java版)-9-N皇后問題LeetCode演算法Java
- 回溯問題Python框架總結——排列組合問題Python框架
- HDU - 2553 N皇后問題(DFS)
- 7-22 n queens (10分) 八皇后(n皇后)問題
- 2020-11-18 N皇后問題
- N皇后問題(各種優化)優化
- 【力扣】排列問題(回溯法)(去重)力扣
- 遞迴解決全排列問題遞迴
- 資料結構和演算法——遞迴-八皇后問題(回溯演算法)資料結構演算法遞迴
- 回溯問題
- 每日一題之拉低通過率 回溯演算法 leetcode 51 N皇后每日一題演算法LeetCode
- 【LeetCode回溯演算法#07】子集問題I+II,鞏固解題模板並詳解回溯演算法中的去重問題LeetCode演算法
- 回溯演算法 | 追憶那些年曾難倒我們的八皇后問題演算法
- leetcode.回溯演算法能解決什麼問題?LeetCode演算法
- 【問題解決】使用YYYY-MM-dd時間轉換問題
- 演算法學習回顧-皇后問題演算法
- 洛谷八皇后問題
- 八皇后問題python解法Python
- C#資料結構與演算法系列(十四):遞迴——八皇后問題(回溯演算法)C#資料結構演算法遞迴
- Leetcode 通過率最高的困難題 N皇后 II 【回溯解法-剪枝】LeetCode
- [Leetcode]827.使用回溯+標記解決最大人工島問題LeetCode
- RecyclerView的使用總結以及常見問題解決方案View
- [20210625]find -mtime +N N -N時間問題補充.txt
- [20210626]find -mtime +N N -N時間問題補充.txt
- 探尋 JavaScript 精度問題以及解決方案JavaScript
- Webpack的理解以及解決了的問題Web
- vue中常見的問題以及解決方法Vue
- LINUX 解決時間同步問題(NTP)Linux
- 遞迴-*全排列問題遞迴
- 字串排列組合問題字串
- 使用貪心演算法解決集合覆蓋問題演算法
- 使用 Rxjs 解決 Angular Component 之間的通訊問題JSAngular
- [20210624]find -mtime +N N -N的時間範圍問題.txt
- 死鎖問題排查過程-間隙鎖的復現以及解決