演算法(一)——回溯法
阿新 • • 發佈:2019-01-23
回溯法(探索與回溯法)是一種選優搜尋法,又稱為試探法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為"回溯點"。
回溯演算法的基本思想是:從一條路往前走,能進則進,不能進則退回來,換一條路再試。
N皇后問題
【描述】
在n×n格的棋盤上放置彼此不受攻擊的n個皇后。按照國際象棋的規則,皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。n後問題等價於再n×n的棋盤上放置n個皇后,任何2個皇后不妨在同一行或同一列或同一斜線上。
【分析】
要解決N皇后問題,其實就是要解決好怎麼放置這n個皇后,每一個皇后與前面的所有皇后不能在同一行、同一列、同一對角線,在這裡我們可以以行優先,就是說皇后的行號按順序遞增,只考慮第i個皇后放置在第i行的哪一列,所以在放置第i個皇后的時候,可以從第1列判斷起,如果可以放置在第1個位置,則跳到下一行放置下一個皇后。如果不能,則跳到下一列...直到最後一列,如果最後一列也不能放置,則說明此時放置方法出錯,則回到上一個皇后向之前放置的下一列重新放置。此即是回溯法的精髓所在。當第n個皇后放置成功後,即得到一個可行解,此時再回到上一個皇后重新放置尋找下一個可行解...如此後,即可找出一個n皇后問題的所有可行解。
迭代程式碼
不在同一對角線上:
(1)兩個座標之和相等(2)兩個座標之差相等。例如有兩個點作為分別為(a,b)和(c,d),他們在同一對角線上的條件是(1)a+b=c+d (2) a-b=c-d 等價於 |a-b|=|c-d|.
遞迴程式碼int Place(int *Column,int index){ int i; for(i=1;i<index;i++){ int Column_differ=abs(Column[index] – Column[i]); int Row_differ=abs(index - i); if(Column[i] == Column[index] || Column_differ == Row_differ)/*有皇后與其在同列或同一斜線上*/ return 0; } return 1; /*沒有皇后與其同行、同列或同對角線*/ } void N_Queue(int n){ int Column[n + 1]; //<span style="font-size: 18.18181800842285px;">Column[i]=a 代表把皇后存放在第i行的第a個位置。</span> <span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">int </span>index=1; //行數 int i; //迴圈變數 int answer_num=0; //解個數 for(I =1;i <= n;i++) //初始化陣列Column Column[i]=0; while(index > 0){ Column[index]++; while(Column[index] <= n && !Place(Column[index],index)) /*尋找皇后的位置*/ Column[index]++; if(Column[index] <= n){ if(index== n){ //如果是最後一行時,即有一個解時 answer_num++; for(i = 1;i <= n;i++) Column[index]++; } else{ /*繼續尋找下一個皇后*/ index++; Column[index]=0; } } else index--; /*當前皇后無法放置,回溯至上一個皇后*/ } } </span>
int queen(int index) { if(index>n && n>0) //當放置的皇后超過n時,可行解個數加1,此時n必須大於0 answer_sum++; else for(int i=1;i<=n;i++) { Column[index] = i; //標明第t個皇后放在第i列 if(place(index)) //如果可以放在某一位置,則繼續放下一皇后 queen(index+1); } return sum; }