理解回溯法及例题分析

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 

posted @ 2018-12-23 01:14  打死也不吃香菜  阅读(1943)  评论(1编辑  收藏  举报