演算法詳解之回溯法

2021-12-30 03:02:00 字數 2855 閱讀 9646

理論輔助:

回溯演算法也叫試探法,它是一種系統地搜尋問題的解的方法。回溯演算法的基本思想是:從一條路往前走,能進則進,不能進則退回來,換一條路再試。用回溯演算法解決問題的一般步驟為:

1、定義乙個解空間,它包含問題的解。

2、利用適於搜尋的方法組織解空間。

3、利用深度優先法搜尋解空間。

4、利用限界函式避免移動到不可能產生解的子空間。

問題的解空間通常是在搜尋問題的解的過程中動態產生的,這是回溯演算法的乙個重要特性。

還是那個基調,不喜歡純理論的東西,喜歡使用例子來講訴理論,在演算法系列總結:動態規劃(解公司外包成本問題) 的那一節裡面

我們舉得是經典的0-1揹包問題,在回溯演算法裡面也有一些很經典的問題,當然,動態規劃的0-1揹包問題其實也可以使用回溯演算法來解。在諸如此類似的求最優解的問題中,大部分其實都可以用回溯法來解決,可以認為回溯演算法乙個」通用解題法「,這是由他試探性的行為決定的,就好比求乙個最優解,我可能沒有很好的概念知道怎麼做會更快的求出這個最優解,但是我可以嘗試所有的方法,先試探性的嘗試每乙個組合,看看到底通不通,如果不通,則折回去,由最近的乙個節點繼續向前嘗試其他的組合,如此反覆。這樣所有解都出來了,在做一下比較,能求不出最優解嗎?

例子先行,現在我們來看看經典的n後問題

問題描述:在n*n格的棋盤上放置彼此不受攻擊的n個皇后。按照西洋棋的規矩,皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。n後問題等價於在n*n格的棋盤上方置n個皇后,任何2個皇后不放在同一行或同一列或同一斜線上。我們需要求的是可放置的總數。

基本思路: 用n元組x[1;n]表示n後問題的解。其中,x[i]表示皇后i放置在棋盤的第i行的第x[i]列。由於不容許將2個皇后放在同一列上,所以解向量中的x[i]互不相同。2個皇后不能放在同一斜線上是問題的隱約束。對於一般的n後問題,這一隱約束條件可以化成顯約束的形式。如果將n*n

格的棋盤看做二維方陣,其行號從上到下,列號從左到右依次編號為1,2,...n。從棋盤左上角到右下角的主對角線及其平行線(即斜率為-1的各斜線)上,2個下標值的差(行號-列號)值相等。同理,斜率為+1的每條斜線上,2個下標值的和(行號+列號)值相等。因此,若2個皇后放置的位置分別是(i,j)和(k,l),且 i-j = k -l 或 i+j = k+l,則說明這2個皇后處於同一斜線上。以上2個方程分別等價於i-k = j-l 和 i-k =l-j。由此可知,只要|i-k|=|l-j|成立,就表明2個皇后位於同一條斜線上。

1、從空棋盤起,逐行放置棋子。

2、每在乙個布局中放下乙個棋子,即推演到乙個新的布局。

3、如果當前行上沒有可合法放置棋子的位置,則回溯到上一行,重新布放上一行的棋子。

**:#include

#include

#include

static int n,x[1000];

static long sum;

int place(int k)

void backtrak(int t)

} int main() }

這段**有必要解釋一下,place(int)即嘗試看是否可以,如果不可以則回退到t+1層,再嘗試其他的組合。

這裡也道出了回溯演算法的核心思想:但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇

演算法實踐:

問題描述:在乙個n*n的網格裡,每個網格可能為「牆壁」(用『x』表示)和「街道」(用『.』表示)。現在在街道放置碉堡,每個碉堡可以向上下左右四個方向**,子彈射程無限遠。牆壁可以阻擋子彈。問最多能放置多少個碉堡,使它們彼此不會互相摧毀。

如下面四張圖,牆壁用黑正方形表示,街道用空白正方形表示,圓球就代表碉堡。1,2,3是正確的,4,5是錯誤的。以為4,5裡面在某一行或者某一列有兩個碉堡,這樣他們就會互相攻擊了。意思明白了嗎?可能我的表達很不清晰,呵呵….

輸入輸出示例

sample input:

——————輸入的n值

.x..

....

xx..

....

xx

.x

.x.

x.x

.x.

....

....

....

....

sample output:

初拿到這個問題,你會不會想到回溯演算法呢?有人說遍歷牆的位置,然後再牆的上下左右四個格仔放置碉堡會得到最優解,這個我沒有驗證過,細細的用筆畫了畫,好像是這麼回事,但是很多時候要知道最優解用什麼方法是很難發現的,利用通用解題方法回溯法,我們可以在一片茫然的時候開始我們的程式設計

首先我們來分析一下這個問題:使用回溯法,我們嘗試每一種可能放置的情況,然後進行判斷是否滿足要求,若不滿足,嘗試放到下乙個單元格,如此反覆,最終,我們將所有可能放置的情況全部遍歷出來了,連所有情況都出來了,難不成還找不到最優解嗎?哈哈。。說做就做…

#include

char map[4][4];

int best,n;

int canput(int row, int col)

for (i = col - 1; i >= 0; i--)

return 1;

}void solve(int k,int tot)

}else

solve(k+1,tot);}}

int main()

return 0;

} 對上面的**做一下點解釋,canput是做檢驗的,檢驗放在某個地點到底行不行得通,solve才是真正進行遞迴回溯的函式。。

演算法之回溯法

回溯法非常適合由多個步驟組成的問題,並且每個步驟都有多個選項。當我們在某一步選擇了其中乙個選項時,就進入下一步,然後面臨新選項,重複選擇,直至最終狀態。經典面試題1 矩陣中的路徑 詳見 劍指offer 面試題12 易錯點 1.由於路徑不能重複進入矩陣的格仔,因此還需定義和字元矩陣大小一樣的布林值矩陣...

演算法之回溯法

求解步驟 1,定義給定問題的解向量解空間 子集樹 排列樹 2,設計剪支函式 限界函式及約束函式 3,深度優先遍歷結合剪支得出解 求解過程 1,是否為完全解,是則輸出 2,是否為部分解,是則進行下乙個解分量的判斷 3,是否為當前可選集合中的最後乙個元素,是則回溯,重新判斷上乙個節點,否則判斷可選集合中...

常用演算法之回溯法

回溯演算法實際上乙個類似列舉的搜尋嘗試過程,主要是在搜尋嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就 回溯 返回,嘗試別的路徑。回溯法是一種選優搜尋法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,...