理解回溯法及例題分析


1、對回溯算法的理解

回溯法(探索與回溯法)是一種選優搜索法,又稱為試探法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為"回溯點"。

(1)回溯法解題時通常包含3個步驟:

①針對所給問題,定義問題的解空間;

② 確定易於搜索的解空間結構;

③以深度優先方式搜索解空間,並在搜索過程中用剪枝函數避免無效搜索。

(2)回溯法的算法框架

①解空間:問題的解空間至少包含問題的一個(最優)解。其表示形式一般是解空間樹:子集樹和排列樹

②遞歸回溯:

 1 void Backtrack(int t)
 2 {
 3     if(t>n) Output(x);
 4     else {
 5         for(int i=f(n,t);i<=g(n,t);i++) {
 6             x[t]=h(i);
 7             if(Constraint(t)&&Bound(t)) Backtrack(t+1);
 8         }
 9     }
10 }

③迭代回溯:

 1 void IterativeBacktrack(void)
 2 {
 3     int t=1;
 4     while(t>0) {
 5         if(f(n,t)<=g(n,t)) {
 6             for(int i=f(n,t);i<=g(n,t);i++) {
 7                 x[t]=h(i);
 8                 if(Constraint(t)&&Bound(t)) {
 9                     if(Solution(t)) Output(x);
10                     else t++;
11                 }
12             }
13         }
14         else t--;          
15     }
16 }

2、例題:子集和問題

(1)子集和問題

設集合S={x1,x2,…,xn}是一個正整數集合,c是一個正整數,子集和問題判定是否存在S的一個子集S1,使S1中的元素之和為c。試設計一個解子集和問題的回溯法。

輸入格式:輸入數據第1行有2個正整數n和c,n表示S的大小,c是子集和的目標值。接下來的1行中,有n個正整數,表示集合S中的元素。

輸出格式:輸出子集和問題的解,以空格分隔,最后一個輸出的后面有空格。當問題無解時,輸出“No Solution!”。

輸入樣例:5 10

                  2 2 6 5 4

輸出樣例:2 2 6

(2)解空間

本題的解空間為:{x1,x2,x3,x4,···,xn},其中xi表示是否加上第i個數

(3)約束函數

本題的約束方式有兩部分,①isC + num[i] <= c,通過判斷當前子集和是否超出題目要求的子集和來剪枝

②isC+total >= c,與第一部分類似

(4)具體代碼

 1 #include <iostream>
 2 using namespace std;
 3 #define N 1000
 4 int n,c,total=0;  // total表示所有整數之和
 5 int isSelect[N]={0};  // 表示整數n是否被選擇,1表示選擇
 6 int num[N];  // 表示整數集
 7 int isC=0;  // 表示當前子集和
 8 
 9 bool sum(int i)
10 {
11     if(isC == c) return true;  // 當子集和符合條件時返回
12     if(i > n) return false;
13     total-=num[i];
14     if(isC + num[i] <= c) {
15         isSelect[i]=1;
16         isC+=num[i];
17         if(sum(i+1)) return true;
18         isC-=num[i];  // 回溯法
19     }
20     if(isC+total >= c) {
21         isSelect[i]=0;  // 回溯法
22         if(sum(i+1)) return true;
23     }
24     total += num[i];  // 回溯法
25     return false;
26 }
27 
28 int main()
29 {
30     cin>>n>>c;
31     for(int i=1;i<=n;i++) {
32         cin>>num[i];
33         total+=num[i];
34     }
35     if(!sum(1)) cout<<"No Solution!";  // 無解時
36     else {
37         for(int i=1;i<=n;i++) {
38             if(isSelect[i]) cout<<num[i]<<" ";
39         }
40     }
41     return 0;
42 } 

3、在本章學習過程中遇到的問題及結對編程的情況

 對於回溯法的概念,結合解空間樹,理解起來不是很難,但是在代碼實踐時,有時候會出現不是很明白為什么要怎么寫代碼的情況,結對小伙伴也不是很明白,總體上還可以,但是還需要多實踐多理解。

參考資料:https://baike.so.com/doc/6735197-6949574.html 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM