1. 程式人生 > >暴力搜尋------回溯法

暴力搜尋------回溯法

       回溯法(backtracking)是深度優先搜尋(DFS)的一種,按照深度優先的順序便利解答樹。應用範圍很廣,只要能把待求解的問題分成不太多的步驟,每個步驟又只有不太多的選擇,都可以考慮應用回溯法。在學習回溯法之前,一定要保證遞迴程式能熟練準確地寫出。

       當把問題分成若干步驟並遞迴求解時,如果當前步驟沒有合法選擇則函式將返回上一級遞迴呼叫。

例如:

       八皇后問題:在棋盤上放置八個皇后,使得它們互不攻擊,每個皇后的攻擊範圍是同行同列同對角線,找出所有解的個數。

       如果把問題相成從64個格子中選8個格子,那麼根據組合數學需要4.426*10^9種方案,但是如果用陣列C[x]表示第x行皇后的列編號

,則問題變成了全排列生成問題。而0~7的全排列一共有8!=40320個,列舉量不會超過它 。

       下面以四皇后問題闡述具體思路:

第一行,皇后可以在第一列、第二列、第三列、第四列。用a[i][j]來表示皇后可能放置的位置,如圖

假設第一行的皇后在a[0][0],那麼在第二行皇后可能放置的合法位置如圖


假設第二行皇后放在a[1][2],那麼第三行無法放皇后,所以回溯到第一行的a[0][0],走下一條路a[1][3],如圖

第四行無法再放置皇后,說明第一個皇后不能在a[0][0],回溯到最開始,讓皇后放置在a[0][1],依此類推得到解答樹。

       觀察解答樹,改變一次行,遍歷四個列(a[1][0]和a[1][1]的情況由於不放置皇后所以沒有畫在圖上,但是這兩種情況的否定需要遍歷判斷),所以只需要用一個一維陣列C[x]來表示第x行皇后的列編號

即可。程式碼如下:

需要在主函式中讀入n(n*n的棋盤),並將解的個數tot清零,然後呼叫Dfs(0),即可求得解。

void Dfs(int cur)                                //cur表示行 
{
	if(cur == n)                             //遞迴邊界,每走到此得到一個解
		tot++;                           //tot表示解的個數 
	else
	{
		for(int i = 0; i < n; i++)       //i表示列 
		{
			int ok = 1;                  //判斷能否放皇后的標誌 
			C[cur] = i;                  //嘗試把第cur行的皇后放在第i列 
			for(int j = 0; j < cur; j++) //j表示行,通過檢查前面幾行判斷能否放皇后 
			{
		                /*判斷列、主對角線、副對角線與前幾行皇后是否衝突*/ 
				if(C[cur]==C[j]||cur-C[cur]==j-C[j]||cur+C[cur]==j+C[j])
				{
					ok = 0;
					break;
				} 
			} 
			if(ok)
				Dfs(cur + 1);
		}
	}
} 


C[cur] == C[j] 、cur - C[cur] == j - C[j]、cur + C[cur] == j + C[j]的理解如下:

由於是逐行放置,所以橫向一定不會衝突,只需檢查是否縱向和斜向衝突即可,在直角座標系中,y = -x與y = x為正斜向線,類比座標系,cur為行,抽象為橫座標,C[cur]為列,抽象為縱座標,由於不一定要通過原點,所以y-x與y+x可以為任意常數,只要這個常數與前一行相等,說明兩皇后斜向衝突。