回溯法採用的搜尋策略 五大常用演算法之四 回溯法

2021-10-12 15:53:40 字數 4009 閱讀 2615

1、概念

回溯演算法實際上乙個類似列舉的搜尋嘗試過程,主要是在搜尋嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就「回溯」返回,嘗試別的路徑。回溯法是一種選優搜尋法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為「回溯點」。 許多複雜的,規模較大的由多步驟組成,並且每一步都有多種選項,當我們在某一步選擇了其中一項時就進入下一項,然後又面臨新的選項之類的問題都可以使用回溯法,有「通用解題方法」的美稱。

2、基本思想

回溯法思想簡單描述:把問題的解空間轉化為圖或者樹的結構表示,然後使用深度優先搜尋策略進行遍歷,遍歷過程中記錄和尋找可行解或者最優解。基本思想類似二叉樹的後序遍歷。

3、用回溯法解題的一般步驟:

(1)針對所給問題,確定問題的解空間:首先應明確定義問題的解空間,問題的解空間應至少包含問題的乙個(最優)解。

(2)確定結點的擴充套件搜尋規則

(3)以深度優先方式搜尋解空間,並在搜尋過程中用剪枝函式避免無效搜尋。

4、演算法框架

(1)問題框架

設問題的解是乙個n維向量(a1,a2,………,an),約束條件是ai(i=1,2,3,…..,n)之間滿足某種條件,記為f(ai)。

(2)非遞迴回溯框架int a[n],i;

初始化陣列a;

i = 1;

while (i>0(有路可走)   and  (未達到目標))  // 還未回溯到頭

if(i > n)                           // 搜尋到葉結點

搜尋到乙個解,輸出;

else                           // 處理第i個元素

a[i]第乙個可能的值;

while(a[i]在不滿足約束條件且在搜尋空間內)

a[i]下乙個可能的值;

if(a[i]在搜尋空間內)

標識占用的資源;

i = i+1;           // 擴充套件下乙個結點

else

清理所佔的狀態空間;   // 回溯

i = i –1;

(3)遞迴的演算法框架

回溯法是對解空間的深度優先搜尋,在一般情況下使用遞迴函式來實現回溯法比較簡單,其中i為搜尋的深度,框架如下: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]置空值等);

5、經典問題

(1)裝載問題

(2)0-1揹包問題

(3)旅行售貨員問題

(4)八皇后問題

(5)迷宮問題

(6)圖的m著色問題

1. 0-1揹包問題

問題:給定n種物品和一揹包。物品i的重量是wi,其價值為pi,揹包的容量為c。問應如何選擇裝入揹包的物品,使得裝入揹包中物品的總價值最大?

#include 

#define n 3         //物品的數量

#define c 16        //揹包的容量

int w[n]=;  //每個物品的重量

int v[n]=;   //每個物品的價值

int x[n]=;   //x[i]=1代表物品i放入揹包,0代表不放入

int curweight = 0;  //當前放入揹包的物品總重量

int curvalue = 0;   //當前放入揹包的物品總價值

int bestvalue = 0;  //最優值;當前的最大價值,初始化為0

int bestx[n];       //最優解;bestx[i]=1代表物品i放入揹包,0代表不放入

//t = 0 to n-1

void backtrack(int t)

//葉子節點,輸出結果

if(t>n-1)

//如果找到了乙個更優的解

if(curvalue>bestvalue)

//儲存更優的值和解

bestvalue = curvalue;

for(int i=0;i

else

//遍歷當前節點的子節點:0 不放入揹包,1放入揹包

for(int i=0;i<=1;++i)

x[t]=i;

if(i==0) //不放入揹包

backtrack(t+1);

else //放入揹包

//約束條件:放的下

if((curweight+w[t])<=c)

curweight += w[t];

curvalue += v[t];

backtrack(t+1);

curweight -= w[t];

curvalue -= v[t];

//ps:上述**為了更符合遞迴回溯的正規化,並不夠簡潔

int main(int argc, char* ar**)

backtrack(0);

printf("最優值:%d\n",bestvalue);

for(int i=0;i

printf("最優解:%-3d",bestx[i]);

return 0;

2. 旅行售貨員問題

3. 詳細描述n皇后問題

問題:在n×n格的棋盤上放置彼此不受攻擊的n個皇后。按照西洋棋的規則,皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。

n皇后問題等價於在n×n格的棋盤上放置n個皇后,任何2個皇后不放在同一行或同一列或同一斜線上。

分析:從n×n個格仔中選擇n個格仔擺放皇后。可見解空間樹為子集樹。

使用board[n][n]來表示棋盤,board[i][j]=0 表示(i,j)位置為空,board[i][j]=1 表示(i,j)位置擺放有乙個皇后。

全域性變數way表示總共的擺放方法數目。

使用queen(t)來擺放第t個皇后。queen(t) 函式符合子集樹時的遞迴回溯正規化。當t>n時,說明所有皇后都已經擺   放完成,這是乙個可行的擺放方法,輸出結果;否則,遍歷棋盤,找皇后t所有可行的擺放位置,feasible(i,j) 判斷皇后t能否擺放在位置(i,j)處,如果可以擺放則繼續遞迴擺放皇后t+1,如果不能擺放,則判斷下乙個位置。

feasible(row,col)函式首先判斷位置(row,col)是否合法,繼而判斷(row,col)處是否已有皇后,有則衝突,返回0,無則繼續判斷行、列、斜方向是否衝突。斜方向分為左上角、左下角、右上角、右下角四個方向,每次從(row,col)向四個方向延伸乙個格仔,判斷是否衝突。如果所有方向都沒有衝突,則返回1,表示此位置可以擺放乙個皇后。

4. 迷宮問題

問題:給定乙個迷宮,找到從入口到出口的所有可行路徑,並給出其中最短的路徑

分析:用二維陣列來表示迷宮,則走迷宮問題用回溯法解決的的思想類似於圖的深度遍歷。從入口開始,選擇下乙個可以走的位置,如果位置可走,則繼續往前,如果位置不可走,則返回上乙個位置,重新選擇另乙個位置作為下一步位置。

n表示迷宮的大小,使用maze[n][n]表示迷宮,值為0表示通道(可走),值為1表示不可走(牆或者已走過);

point結構體用來記錄路徑中每一步的座標(x,y)

(enter_x,enter_y) 是迷宮入口的座標

(exit_x, exit _y)    是迷宮出口的座標

path容器用來存放一條從入口到出口的通路路徑

bestpath用來存放所有路徑中最短的那條路徑

maze()函式用來遞迴走迷宮,具體步驟為:

1. 首先將當前點加入路徑,並設定為已走

2. 判斷當前點是否為出口,是則輸出路徑,儲存結果;跳轉到4

3. 依次判斷當前點的上、下、左、右四個點是否可走,如果可走則遞迴走該點

4. 當前點推出路徑,設定為可走

參考:

回溯法採用的搜尋策略 五大常用演算法之四 回溯法

一 基本描述 類似於回溯法,也是一種在問題的解空間樹t上搜尋問題解的演算法。但在一般情況下,分支限界法與回溯法的求解目標不同。回溯法的求解目標是找出t中滿足約束條件的所有解,而分支限界法的求解目標則是找出滿足約束條件的乙個解,或是在滿足約束條件的解中找出使某一目標函式值達到極大或極小的解,即在某種意...

五大常用演算法 回溯法

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

五大常用演算法之四 回溯法

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