回溯法的基本思想
回溯法有「通用的解題法」之稱。該方法系統地搜尋乙個問題的所有解或任一解。
問題解的表示:回溯法將乙個問題的解表示成乙個n元式(x1,x2,…,xn)的形式。
顯示約束:對分量xi的取值限定。
隱示約束:為滿足問題的解而對不同分量之間施加的約束。
解空間:對於問題的乙個例項,解向量滿足顯式約束條件的所有多元組,構成了該例項的乙個解空間。
回溯法通常將問題解空間組織成「樹」結構,通過採用系統的方法搜尋解空間樹,從而得到問題解。
回溯法的基本做法是搜尋,是一種組織得井井有條的,能避免不必要搜尋的窮舉式搜尋法。
搜尋策略:深度優先、廣度優先、函式優先、廣度深度結合等。
結點分支判定條件:
滿足約束條件:分支擴充套件解向量
不滿足約束條件:回溯到當前結點的父結點
結點狀態:
白結點(尚未訪問)
灰結點(正在訪問以該結點為根的子樹)
黑結點(以該結點為根的子樹遍歷完成)
儲存:當前路徑
回溯法在搜尋解空間時,通常採用兩種策略(剪枝函式)避免無效搜尋:
約束函式:在擴充套件結點處剪去不滿足約束條件的子樹
界限函式:在擴充套件結點處剪去得不到最優解的子樹
回溯法的設計要素
針對問題定**空間:
問題解向量
解向量分量取值集合
構造解空間樹
判斷問題是否滿足多公尺諾性質
搜尋解空間樹,確定剪枝函式
確定儲存搜尋路徑的資料結構
兩類典型的解空間樹
回溯法的程式結構
void backtrack (int t)
}
void iterativebacktrack ()
}else t--;}}
回溯法求解問題例項分析一、裝載問題
有一批共n個貨櫃要裝上2艘載重量分別為c1和c2的輪船,其中貨櫃i的重量為wi,裝載問題要求確定是否有乙個合理的裝載方案可將這個貨櫃裝上這2艘輪船。如果有,找出一種裝載方案。
例如:當n=3, c1=c2=50,且w=[10,40,40]時,可將1和2裝上第一艘輪船,3裝入第二艘輪船;若w=[20,40,40],則無法將這三個貨櫃都裝上輪船。
分析
容易證明,如果乙個給定裝載問題有解,則採用下面的策略可得到最優裝載方案。
(1)首先將第一艘輪船盡可能裝滿;
(2)將剩餘的貨櫃裝上第二艘輪船。
將第一艘輪船盡可能裝滿等價於選取全體貨櫃的乙個子集,使該子集中貨櫃重量之和與第一艘輪船載重量最接近。由此可知,裝載問題等價於特殊的0-1揹包問題。
解空間:子集樹
可行性約束函式(選擇當前元素):cw+w[i]<=capacity (之前的載重量加上當前元素的重量小於等於輪船最大載重)
上界函式(不選擇當前元素):
當前載重量cw+剩餘貨櫃的重量r <= 當前最優載重量bestw ( 即如果當前載重量加上剩餘子樹中所有物品的重量都沒有最優載重大,則當前元素及其子樹可以不再搜尋(剪枝),因為及時到達葉節點也得不到最優解。)
例項計算過程
**實現
/**
* 裝載問題
* *@author luwei
*@version
*//**
* 回溯法實現裝載類
* */
final class loading
bestw = cw;//更新最優解
}return;
}//搜尋子樹
rweight -= weights[i];
if(cw+weights[i] <= capacity)
if(cw + rweight > bestw)
//返回父節點
rweight += weights[i];
}/**
* 獲取最大裝載量
**@return 最大裝載量
*/public
intgetmaxload()
/*** 獲取裝載方案
**@return 裝載方案,物品裝載與否向量
*/public
int getloadingplan()
}/**
* 求解裝載問題類
*/public
final
class
load
public
static
void
main(string args);
// int w = ;
int c = 152;
int plan = new
int[w.length];
system.out.print("weights: " + w[0]);
for(int i=1; i", " + w[i]);
}system.out.println();
system.out.println("capacity: " + c);
system.out.println("maxload: " + maxloading(w, c, plan));
system.out.print("loading plan: " + plan[0]);
for(int i=1; i", " + plan[i]);
}system.out.println();}}
二、n皇后問題
問題描述
在n×n的棋盤中放置n個皇后,使得任何兩個皇后之間不能相互攻擊(不在同行同列或者對角線),試給出所有的放置方法。
分析
1)問題解向量:(x1, x2, … , xn)
2)顯約束:xi=1,2, … ,n
3)隱約束:
(1)不同列:xixj;
(2)不處於同一正、反對角線:|i-j||xi-xj|
回溯函式
void backtrack(int t)
}
判斷是否可以放置
bool place(int k)
演算法時間複雜度(1)搜尋1+n+n^2+…+n^n=(n^(n+1)-1)/n-1≤2n^n;
(2)每個節點判斷是否符合規則,最多要判斷3n個位置(列方向、主與副對角線方向是否有皇后)
故最壞情況下時間複雜度o(3n×2n^n)=o(n^(n+1))
**實現
/**
* 皇后問題
* *@author lsj
*@version
*/final class placing
/*** 遞迴回溯函式
**@param d 子集樹求解深度
*/public
void
backtrack(int d)
else}}
/*** 判斷位置可行函式,約束函式
**@param p 棋盤列位置
*/private
boolean
placeok(int p)
return
true;
}private
void
outputx()
}public
final
class
queen
public
static
void
main(string args)
}
實驗五 回溯法
實驗 五 回溯法 一 實驗目的與要求 1 通過回溯法的示例程式理解回溯法的基本思想 2 運用回溯法解決實際問題進一步加深對回溯法的理解和運用 二 實驗內容 1 分析並掌握 符號三角 問題的回溯法求解方法 2 分析並掌握 n皇后 問題的回溯演算法求解方法 3 練習使用回溯法求解 整數變換 等問題。三 ...
演算法設計與分析基礎4 回溯策略
遞迴與分治演算法動態規劃貪心演算法回溯演算法回溯,又可以說深度優先搜尋,暴搜,是一種通用的求解框架。回溯法求解一般步驟 1.定義問題的解空間 2.確定易於搜尋的解空間結構 3.以dfs的方式搜尋解空間,用剪枝函式 減去子樹或限界函式 避免無效搜尋。既然是dfs就可以由遞迴和非遞迴 迭代 兩種形式來表...
演算法分析與設計實驗報告四 回溯法實驗
一 實驗目的 掌握回溯演算法思想 掌握回溯遞迴原理 了解回溯法典型問題 二 實驗內容 編寫乙個簡單的程式,解決8皇后問題。數字全排列問題 任意給出從1到n的n個連續的自然數,求出這n個自然數的各種全排列。如n 3時,共有以下6種排列方式 123,132,213,231,312,321。注意 數字不能...