第五章回溯法小结

第五章回溯法

作用:当需要找到问题的解集或者要求回答什么解释满足某些约束条件的最佳解时,往往要使用到回溯法

做法:1)回溯法的基本做法是搜索,或是一种组织得井井有条的,能避免不必要搜索的穷举式搜索法。这种方法适用于解一些组合数相当大的问题。

   2)回溯法在问题的解空间树种,按深度优先策略,从根节点出发搜索解空间树,算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解,如果肯定不包含,则跳过对该结点为跟的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。

      3)①若求问题所有的解时,要回溯到根,且根节点的所有可行的子树都要已被搜索遍才结束

      ②若求任一个解时,只要搜索到问题的一个解就可以结束

回溯法的算法框架:

①针对所给的问题,定义问题的解空间

解空间:复杂问题的很多可能的解构成了这个问题的解空间 解空间就是进行穷举的搜索空间

eg.对于有n个物品的0/1背包问题 当n=3时  解空间是:{(0,0,0)(0,0,1)(0,1,0)(1,0,0)(0,1,1)(1,0,1)(1,1,0)(1,1,1)}

②确定易于搜索的解空间结构

③以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索

常用剪枝函数:

①用约束函数扩展结点处剪去不满足约束的子树

②用限界函数剪去得不到最优解的子树

5-1子集和问题

1)问题描述

 

2)完整代码

 

 

 

 1 #include<iostream>
 2 #include<cstdlib>
 3 using namespace std;
 4 
 5 int n,c;//一共有n个数字 子集和为c
 6 int *numbers;//记录每个数字
 7 int *x;//记录该数字是否选择
 8 int remain;//记录剩余数字的总和
 9 int sum=0;//记录子集和 
10 
11 void backtrack(int t){
12     if(t>n){
13         if(sum == c){
14             for(int i=1;i<=n;i++){
15                 if(x[i]==1)
16                     cout << numbers[i] <<" ";
17             }
18             cout << endl;
19             exit(0);
20         }
21         return;
22     }
23     
24     remain -= numbers[t];
25     if(sum + numbers[t] <=c){
26         sum += numbers[t];
27         x[t] = 1;
28         backtrack(t+1);
29         sum -= numbers[t];
30     }
31     //不选择当前的数字,遍历右子树
32     //如果当前数字和加上所有剩余数字和仍小于前面访问过的叶子节点的数字和,则剪枝 
33     if(sum + remain >=c){
34         x[t] = 0;
35         backtrack(t+1);
36     }
37     remain += numbers[t];
38 } 
39 
40 int main(){
41     
42     cin >> n >> c;
43     numbers = new int[n+1];
44     remain =0;
45     for(int i=1;i<=n;i++){
46         cin >> numbers[i];
47         remain += numbers[i];
48     }
49     if(remain<c){
50         cout <<"No Solution!"<<endl;
51         return 0;
52     }
53     x = new int[n+1];
54     backtrack(1);
55     cout << "No Solution!"<<endl;
56     return 0;
57 } 
子集和问题的完整代码

3)解空间结构:在所有的子集和中搜索,

①但是运用了约束条件函数,当当前遍历的值与子集和的值加起来 如果已经超过了c 则无需再加入此节点,这就减少了解空间的大小

1 if(sum + numbers[t] <=c){
2         sum += numbers[t];
3         x[t] = 1;
4         backtrack(t+1);
5         sum -= numbers[t];
6     }
约束函数

②运用了剪枝函数,如果当前的子集和的值 加上剩余的所有的数的值都加起来 ,已经超过了所需的子集和的值c就没用必要再遍历下去了

1 if(sum + remain >=c){
2         x[t] = 0;
3         backtrack(t+1);
4     }
剪枝函数

③还有一个剪枝函数,就是当你所有的值加起来都小于你所要的子集和,就更加没有必要遍历下去了 因为你一定是没有符合条件的解的

1     if(remain<c){
2         cout <<"No Solution!"<<endl;
3         return 0;
4     }
所有值加起来小于子集和的剪枝函数

遇到的困难:

在本章学习中 ,遇到的困难还挺多的,①在多次的backtrack过程中 有点难理解递归的含义 ,有时会有些混乱

②刚开始无法理解剪枝函数和约束函数的区别,但是后来在重新学习了子集和问题之后,有些弄懂了,

约束函数:为了减少解空间的大小,将不符合条件的结点排除在外,就无需将该结点加在解空间中,从而减少了解空间的大小

剪枝函数:如果当前子集和中的值加上剩余的值已经大于所需的子集和,就无需遍历以此节点为根的子树了

结对编程的情况:

这次结对编程时 我们一道题都没打出来 主要是我们两个对回溯法都不是很理解,当时在做的时候虽然看到老师有演示和讲一遍但是还是很蒙,后来我们回去之后又继续摸索了一下,总算弄懂了。但总的来说这学期的结对编程还是很愉快的,我在我的搭档张雯静身上学到了很多,她思维很活跃很快,我反应比较慢 她也总是能很耐心的给我讲解。

posted on 2019-12-19 11:46  流星雨lxy  阅读(224)  评论(0编辑  收藏  举报

导航