回溯算法
一、对回溯算法的理解:
回溯算法首先得确定一下隐式存在的空间树,然后对其进行深度优先搜索,从根节点出发,根结点为一开始的活节点,每次搜索至空间树的任意结点,判断该节点是否包含问题的结,如果包含,则继续深度优先搜索下去,否则,该节点视为死结点,跳过该节点,回溯回到祖先结点(活节点),再将这个活节点视为当前的扩展结点进行深度优先搜索。求问题的所有解时,要回溯到根并且根结点的所有子树都被搜索才能结束,而在求一个解时,搜索到一个最优解便可以结束。
二、“子集和”问题的解空间和约束函数:
问题描述:
设集合S={x1,x2,…,xn}是一个正整数集合,c是一个正整数,子集和问题判定是否存在S的一个子集S1,使S1中的元素之和为c。
试设计一个解子集和问题的回溯法。
这里设 回溯过程中子集和为ans,要求的子集和为c大集合的元素个数为n;
解空间:构建一个深度为n+1的空间树,第二至n层为子集的每一个元素的选择。
约束函数:当ans>c时不再遍历该节点以下的节点了,直接是该节点为死结点,进行剪枝,回溯到最近的祖先节点,再进行深度优先搜索。
附上代码:
1 #include<iostream> 2 using namespace std; 3 int a[10000]; 4 int n,c; 5 int ans=0; 6 bool vis[10000]; 7 bool flag=false; 8 int tip=1; 9 void dfs(int i) 10 { 11 if(flag)return; 12 if (c<ans) return; 13 if(c==ans) 14 { 15 tip=0; 16 for(int i=0;i<n;i++) 17 { 18 if(vis[i]) 19 cout<<a[i]<<' '; 20 } 21 flag=true; 22 return; 23 } 24 if(i>=n||c<ans) 25 return; 26 else 27 { 28 ans+=a[i]; 29 vis[i]=true; 30 dfs(i+1); 31 ans-=a[i]; //从这开始往下一直回溯 32 vis[i]=false; 33 dfs(i+1); 34 } 35 } 36 int main() 37 { 38 int sum=0; 39 scanf("%d%d",&n,&c); 40 for(int i=0;i<n;i++) 41 scanf("%d",&a[i]),sum+=a[i]; 42 if(sum<c) 43 cout<<"No Solution!"<<endl; 44 else 45 { 46 dfs(0); 47 if(tip) 48 cout<<"No Solution!"<<endl; 49 } 50 return 0; 51 }
三、学习过程中的问题和结对编程的情况:
学习问题:
在解决问题的时候,有一些比较抽象,很难处理清晰思路,就算知道怎么做了,还是无法用递归实现,但是非递归方法写起来也很难而且复杂,我觉得关键点还是对递归不够熟悉,最容易理解的方法还是通过解的空间进行几次遍历演示会比较好的理解递归和回溯的求解过程。
结对编程:
结对编程提供了很好的一个探讨的机会,相互解决疑难,相当不错,真的代码自己打和两个人一起探讨研究思路完全是不一样的,如果你是大牛那另说,否则两个人一起探讨问题远远比一个人自己冥思苦想好,而且解决问题速度又快。