八皇后問題
將n個皇后放置在n*n的國際象棋棋盤上,其中沒有任何兩個皇后處於同一行,同一列或者同一對角線上,以使得的它們不能相互攻擊。ios
問題分析
若是這樣表示的話,那麼判斷條件應該如何表示呢?算法
回溯法
當把問題分紅了若干步驟並遞歸求解時,若是當前步驟沒有合理的選擇時,則函數將返回上一級遞歸調用,這種現象叫回溯。正是由於這個緣由,遞歸枚舉算法經常被稱爲回溯法 數組
源碼
#include<iostream> #include<stdio.h> using namespace std; int n = 8; int c[8]={0}; int count = 0; void search(int cur); int main(){ search(0); } void print(){ printf("\n-----------------\n"); for(int i = 0; i < 8; i++){ for(int j = 0; j < 8; j++){ if(c[i] == j){ printf("|*"); }else{ printf("| "); } } printf("|\n-----------------\n"); } } void search(int cur){ if(cur == n){ print(); printf("\n"); }else{ for(int i = 0; i < n; i++){ c[cur] = i; int ok = 1; for(int j = 0; j < cur; j++){ if(c[cur] == c[j] || j - c[j] == cur - c[cur] || j + c[j] == cur + c[cur]){ ok=0; break; } } if(ok){ search(cur+1); } } } }
算法改進
上述算法的枚舉的結點數適合很難減小了,可是程序的效率能夠繼續提升,利用二維數組直接判斷當前嘗試的皇后所在的列和兩個對角線是否已有其餘的皇后,注意的問題是,主對角線的表示y-x可能爲負值,存取時要加上n。函數
void searchs(int cur){ if(cur == n){ count++; }else{ for(int i = 0; i < n; i++){ if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]){ c[cur]=i; vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1; searchs(cur + 1); vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0; } } } }
上述程序有個極其關鍵的地方:vis數組的使用。它表示的含義是已經放置的換後佔據了哪些列,主對角線和副對角線。通常的,若是在回溯法中修改了輔助全局變量,則通常要把它們及時恢復原狀,有多個地方修改,每一個地方都要恢復原有的值。
有興趣的話,能夠研究一下n皇后問題,看一看有沒有什麼規律或者快速解法呢?性能