reference:
http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html
http://www.cnblogs.com/Creator/archive/2011/05/20/2052341.html 1、什麼是回溯法
回溯法是一種系統地搜尋問題解答的方法。在搜尋的過程中嘗試找到問題的解,如果發現找不到了,就退一步,往上回溯(剪枝過程)。對於許多複雜問題和大規模問題都可以使用回溯法。
回溯法的基本思想是按照深度優先搜尋的策略,從根節點開始搜尋,當到某個節點時要判斷是否是包含問題的解,如果包含就從該節點繼續搜尋下去,如果不包含,就向父節點回溯。若用回溯法求問題的所有解時,要回溯到根,且根結點的所有可行的子樹都要已被搜尋遍才結束。而若使用回溯法求任一個解時,只要搜尋到問題的一個解就可以結束。
回溯法常用的剪枝函數:(1)約束函數:在節點處減去不滿足約束的子樹。(2)界限函數:減去得不到最優解的子樹 2、回溯法解題的一般步驟 針對所給問題,確定問題的解空間 利用適於搜尋的方法組織解空間 利用深度優先搜尋解空間 在搜尋過程中用剪枝函數避免無效搜尋。 3、演算法架構 設問題的解是一個n維向量(a1,a2,………,an),約束條件是ai(i=1,2,3,…..,n)之間滿足某種條件,記為f(ai)。 非遞迴回溯架構
int a[n], i;初始化a[n];i = 1;while (i > 0(有路可走) and (未達到目標))//還沒有回溯到頭{ if (i > n) { 搜尋到一個解,輸出; } else { a[i]第一個可能的值; while (a[i]在不滿足約束條件且在搜尋空間內) { a[i]下一個可能的值; } if (a[i]在搜尋空間內) { 標識佔用的資源; i = i + 1; //擴充下一個節點 } else { 清理所佔的空間狀態; //回溯 i = i - 1; } }} 遞迴回溯架構
int a[n];try(int i){ if(i>n) 輸出結果; else { for(j = 下界; j <= 上界; j=j+1) // 枚舉i所有可能的路徑 { if(fun(j)) // 滿足限界函數和約束條件 { a[i] = j; ... // 其他動作 try(i+1); 回溯前的清理工作(如a[i]置空值等); } } } } 4舉例:N皇后問題
在一個N*N的棋盤上放置N個皇后,每行一個並使其不能互相攻擊(同一行、同一列、同一斜線上的皇后都會自動攻擊)。
/*用n元組x[1:n]表示n後問題的解。x[i]表示皇后i放置在棋盤的第i行的第x[i]列*/#include <stdio.h>#include <math.h>#include <stdlib.h>static int n, x[1000];static long sum;/*判斷第k個後能不能放在x[k]處兩個皇后不能放在統一斜線上:若2個皇后放置的位置分別是(i,j)和(k,l),且 i-j = k -l 或 i+j = k+l,則說明這2個皇后處於同一斜線上。*/void OutPut(){ for (int i = 1; i <= n; ++i) printf("(%d, %d) ", i, x[i]); printf("\n");}int Place(int k){ for (int j = 1; j < k; ++j) if (abs(k - j) == abs(x[k] - x[j]) || x[j] == x[k]) return 0; return 1;}void BackTrack1(int t){ //如果t>n說明已經完成一次放置 if (t > n) { sum++; OutPut(); } else { for (int i = 1; i <= n; ++i) { x[t] = i; if (Place(t)) //可以放在i位置處,則繼續搜尋 BackTrack1(t + 1); } }}void BackTrack(){ int k; x[1] = 0; //初始化為0 k = 1; while (k >= 1) //迴圈 { x[k] += 1; //先放在第一個位置 while ((x[k] <= n) && !(Place(k))) //如果不能放 x[k] += 1; //放在下一個位置 if (x[k] <= n) //放置完成 { if (k == n) //如果已經放完了n個皇后 { sum++; //處理次數,輸出 OutPut(); } else //沒有處理完,讓k自加,處理下一個皇后 { k++; x[k] = 0; } } //x[k] > n,說明沒有合適的位置了 else k--; //回溯回去,回到第k-1步 }}int main(){ clock_t start, finish; double duration; int nn; while (scanf_s("%d", &nn) != EOF) { n = nn; sum = 0; for (int i = 0; i <= n; ++i) x[i] = 0; BackTrack(); //BackTrack1(1); printf("%d\n", sum); } return 0;}