回溯法
阿新 • • 發佈:2020-07-26
使用剪枝函式的深度優先生產狀態空間樹中結點的求解方法稱為回溯法。
- 演算法搜尋至任一結點時,先判斷以該結點為根的子樹是否包含問題的解。如果肯定不包含,則跳過對該結點為根的子樹的搜尋,逐層向其父親結點回溯;否則,進入該子樹,繼續按深度優先策略搜尋。
- 為提高搜尋效率,在搜尋過程中用約束函式和限界函式(統稱剪枝函式)來剪去不必要搜尋的子樹,減少問題求解所需實際生成的狀態空間樹結點數,從而避免無效搜尋。
n-皇后
問題:尋找在棋盤(nXn)上放置這n個皇后的方案,使得他們中的任何兩個都不在同一行、同一列或同一斜線上。
- 顯示約束:1)xi=0,1,2,…,n-1 ;2)不同列:xi≠xj
- 隱式約束:不處於同一正、反對角線:|i−j|≠|xi−xj|(i≠j)
- xi=k表示第i+1個皇后放置在第k列
對應解空間為n!
typedef long long int ll; int N,cot;///N分別代表棋盤尺寸(皇后數量)和皇后可能成功擺放的結果總數 int size[11];//陣列的下標表示行,對應的值表示列,N<=10 int ans[11]; // 用於打表存放結果 bool Place(int k,int i) { for(int j=1;j<k;j++)///j從第一行開始 {n皇后if(size[j]==i||((abs(j-k))==abs(size[j]-i))) return false; } return true; } void dfs(int k)///k表示在第幾行 { for(int i=1;i<=N;i++)///i表示在第幾列 { if(Place(k,i)) { size[k]=i; ///代表將第一個皇后放置在第1行第i列(或者說第n個皇后) if(k==N+1)///當最後一個皇后成功放置時返回. { cot++;///成功的次數加一 return ; } else dfs(k+1);///如果放置在第i列不衝突,則開始放下一個皇后 } } } int main() { for( N=1;N<=11;N++)///因為本題時間限制,所以需要先打表 { cot=0;///臨時存放結果 dfs(1);///從第一行開始放置 ans[N]=cot;///將結果依次放入表中 } while(cin >>N) { if(!N) break; cout <<ans[N]<<endl; } return 0; }
子集和問題
給定一個含有n個元素的整形陣列,再給定一個和sum,求出陣列中滿足給定和的所有元素組合存在一個數組中,求滿足的集合有幾個。
對於陣列中任意一個元素,先將其放入結果集中,如果當前和不超出給定和,那就繼續考察下一個元素,如果超出給定和,則捨棄當前元素。如此往復,直到找到所有可行解。
const int maxn=1000; int w[maxn];///儲存所有元素 bool vis[maxn];///判斷該元素是否被加入子集 int M,n;///M是指定的和,n是包含的整數個數 void SUMOFSUB(int s, int k, int r) {///s是已選擇的元素和,k是即將選擇的元素下標,r是剩餘元素和 int j; vis[k] = true; if (s + w[k] == M) { for (j = 1; j <= k; j++) { if(vis[k]) cout << w[k]<< " "; } cout << endl; } else if (s + w[k] + w[k + 1] <= M) { SUMOFSUB(s + w[k], k + 1, r - w[k]);///搜尋左子樹 } if ((s + r - w[k] >= M) && (s + w[k + 1] <= M))///搜尋右子樹 { vis[k]=false;///回溯時先還原 SUMOFSUB(s, k + 1, r - w[k]); } } int main() { int i,r; w[0] = 0; cin>>n>>M; for (i=1,r=0;i <= n;i++) { cin>>w[i]; r += w[i]; } memset(vis,false,sizeof(vis)); SUMOFSUB(0, 1, r); return 0; }子集和