1.针对给定的问题,需要找到行之有效的算法解决。学习和研究前人给出的算法,是具备运用算法和设计算法能力的途径。这其中包括几个层次:①:知道一个已有的算法解决特定的问题;②:从几个算法中,选择一个最合适的去解决给定的问题;③:改进已有的算法使其更适合解决当前问题;④:设计算解决别人没有解决过的问题。
2.常见的算法设计策略包括:分治,动态规划,贪心,回溯,穷举,分支限界,迭代,递归,回溯,
①.分治
分治法的设计思想是,将一个难以直接解决的大问题,分割成k个规模较小的子问题,这些子问题相互独立,且与原问题相同,然后各个击破,分而治之。
分治法常常与递归结合使用:通过反复应用分治,可以使子问题与原问题类型一致而规模不断缩小,最终使子问题缩小到很容易求出其解,由此自然导致递归算法。
根据分治法的分割原则,应把原问题分割成多少个子问题才比较适宜?每个子问题是否规模相同或怎样才为适当?这些问题很难给与肯定的回答。但人们从大量实践中发现,在使用分治法时,最好均匀划分,且在很多问题中可以取k=2。这种使子问题规模大致相等的做法源自一种平衡子问题的思想,它几乎总是比使子问题规模不等的做法好。
分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。
分治法所能解决的问题一般具有以下几个特征:
1) 该问题的规模缩小到一定的程度就可以容易地解决
2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。(前提)
3) 利用该问题分解出的子问题的解可以合并为该问题的解;(最关键的一点)
4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
上述的第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
分治法的基本步骤:
分治法在每一层递归上都有三个步骤:
1)分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
2)解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
3)合并:将各个子问题的解合并为原问题的解。
②.动态规划
动态规划法与分治法类似,其基本思想也是将原问题分解成若干个子问题。与分治法不同的是,其分解出的子问题往往不是相互独立的。这种情况下若用分治法会对一些子问题进行多次求解,这显然是不必要的。动态规划法在求解过程中把所有已解决的子问题的答案保存起来,从而避免对子问题重复求解。
动态规划常用于解决最优化问题。对一个最优化问题可否应用动态规划法,取决于该问题是否具有如下两个性质:
1.最优子结构性质
当问题的最优解包含其子问题的最优解时,称该问题具有最优子结构性质。
要证明原问题具有最优子结构性质,通常采用反证法。假设由问题的最优解导出的子问题的解不是最优的,然后再设法说明在该假设下可构造出比原问题的最优解更好的解,从而导致矛盾。
2.子问题重叠性质
子问题重叠性质是指由原问题分解出的子问题不是相互独立的,存在重叠现象。
用动态规划法解题过程中,应当先找出最优解的结构特征,即原问题的最优解与其子问题的最优解的关联。然后有如下两种程序设计方法:
1.自底向上递归法
利用问题的最优子结构性质,以自底向上的方式递归地从子问题的最优解逐步构造出整个问题的最优解。
2.自顶向下递归法(即备忘录法)
利用问题的最优子结构性质,用与直接递归法相同的控制结构自顶向下地进行递归求解。初始时在表格中为每个子问题存入一个标识解。在求解过程中,对每个待求子问题,首先查看表格中相应的记录项。若记录项为初始时的标识值,则表示该子问题是初次遇到,此时应利用问题的最优子结构性质进行递归求解,并将结果存入表格,以备以后查看。否则则说明该问题已被求解过,直接返回表格中相应的值即可,不必重新计算。
当一个问题的所有子问题都要求解时,应当用自底向上递归法。当子问题空间中的部分子问题可不必求解时,自底向上递归法会进行多余的计算,此时应采用自顶向下递归法。
③.贪心
当一个问题具有最优子结构性质时,可用动态规划法求解。但有时会有比动态规划更简单更直接效率更高的算法——贪心法。贪心法总是做出在当前看来最好的选择,也就是说贪心法并不从整体最优考虑,它所做出的选择只是在某种意义上的局部最优选择。虽然贪心法并不能对所有问题都得到整体最优解,但是对许多问题它能产生整体最优解。有些情况下,贪心法虽然不能得到整体最优解,但其最终结果却是最优解的很好的近似。
贪心算法的基本思路:
1.建立数学模型来描述问题。
2.把求解的问题分成若干个子问题。
3.对每一子问题求解,得到子问题的局部最优解。
4.把子问题的解局部最优解合成原来解问题的一个解。
贪心法常用于解决最优化问题。对一个最优化问题可否应用贪心法,取决于该问题是否具有如下两个性质:
1.贪心选择性质
贪心选择性质是指原问题总有一个整体最优解可通过当前的局部最优选择,即贪心选择来达到。
对于一个具体问题,要确定它是否具有贪心选择性质,通常可考察问题的一个整体最优解,并证明可修改这个最优解,使其以贪心选择开始。由此证明该问题总有一个最优解可通过贪心选择得到,即具有贪心选择性质。
2. 最优子结构性质
这一点与动态规划相同。做出贪心选择后,由于最优子结构性质,原问题简化为规模更小的类似子问题。如果将子问题的最优解和之前所做的贪心选择合并,则可得到原问题的一个最优解。
贪心问题的整体最优解可通过一系列局部的最优选择,即贪心选择来达到。这也是贪心法与动态规划的主要区别。在动态规划中,每一步所做出的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才能做出选择。而在贪心法中,仅做出当前状态下的最好选择,即局部最优选择。然后再去解做出这个选择之后产生的相应的子问题。贪心法所做出的贪心选择可以依赖于以往所做过的选择,但绝不依赖于将来所做的选择,也不依赖于子问题的解。正是由于这种差别,动态规划通常以自顶向上的方式解各子问题,而贪心法通常以自顶向下的方式进行,以迭代的方式做出相继的贪心选择,每做出一次贪心选择就将所求问题简化为规模更小的子问题。
④.回溯
回溯法是对问题的解空间树进行深度优先搜索 ,但是在对每个节点进行DFS之前,要先判断该节点是否有可能包含问题的解。如果肯定不包含,则跳过对以该节点为根的子树的搜索,逐层向其祖先节点回溯。如果有可能包含,则进入该子树,进行DFS。
回溯法通常的解题步骤如下:
(1)定义问题的解空间。
(2)将解空间组织成便于进行DFS的结构,通常采用树或图的形式。
(3)对解空间进行DFS,并在搜索过程中用剪枝函数避免无效搜索。
用回溯法解题时并不需要显式地存储整个解空间,而是在DFS过程中动态地产生问题的解空间。在任何时刻,算法只保存从根节点到当前节点的路径。如果解空间树的高度为h,则回溯法的空间复杂度通常为O(h)
用回溯法解题时,常会遇到以下两类典型的解空间树:
(1)当所给的问题是从n个元素的集合S中找出S满足某种性质的子集时,相应的解空间树称为子集树,例如http://www.cnblogs.com/laifeiyao/p/3481800.html
(2)当所给的问题是找出n个元素满足某种性质的排列时,相应的解空间树称为排列树,例如http://www.cnblogs.com/laifeiyao/p/3492758.html
回溯法中的剪枝函数通常分为两类:
(1)用约束函数在指定节点处剪去不满足约束的子树,例如http://www.cnblogs.com/laifeiyao/p/3481800.html
(2)用限界函数在指定节点处剪去得不到最优解的子树,例如http://www.cnblogs.com/laifeiyao/p/3492758.html
⑤.分支限界
(1)分支搜索算法
所谓“分支”就是采用广度优先的策略,依次搜索E-结点的所有分支,也就是所有相邻结点,抛弃不满足约束条件的结点,其余结点加入活结点表。然后从表中选择一个结点作为下一个E-结点,继续搜索。
选择下一个E-结点的方式不同,则会有几种不同的分支搜索方式。
1)FIFO搜索
2)LIFO搜索
3)优先队列式搜索
(2)分支限界搜索算法
分支限界法的一般过程
由于求解目标不同,导致分支限界法与回溯法在解空间树T上的搜索方式也不相同。回溯法以深度优先的方式搜索解空间树T,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树T。
分支限界法的搜索策略是:在扩展结点处,先生成其所有的儿子结点(分支),然后再从当前的活结点表中选择下一个扩展对点。为了有效地选择下一扩展结点,以加速搜索的进程,在每一活结点处,计算一个函数值(限界),并根据这些已计算出的函数值,从当前活结点表中选择一个最有利的结点作为扩展结点,使搜索朝着解空间树上有最优解的分支推进,以便尽快地找出一个最优解。
(另意:分支限界法的搜索策略是,在当前节点处,先生成其所有的子节点(分支),并为每个满足约束条件的子节点计算一个函数值(限界),再将满足约束条件的子节点全部加入解空间树的活结点优先队列。然后再从当前的活节点优先队列中选择优先级最大的节点(节点的优先级由其限界函数的值来确定) 作为新的当前节点。重复这一过程,直到到达一个叶节点为止。所到达的叶节点就是最优解。 类似于回溯法,也是一种在问题的解空间树T上搜索问题解的算法。但在一般情况下,分支限界法与回溯法的求解目标不同。回溯法的求解目标是找出T中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。)
分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。问题的解空间树是表示问题解空间的一棵有序树,常见的有子集树和排列树。在搜索问题的解空间树时,分支限界法与回溯法对当前扩展结点所使用的扩展方式不同。在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,那些导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被子加入活结点表中。此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。这个过程一直持续到找到所求的解或活结点表为空时为止。
回溯法是对解空间进行深度优先搜索,事实上任何搜索遍整个解空间的算法均可解决问题。所以采用通用图搜索(树可抽象为特殊的图)的任何实现作为搜索策略均可解决问题,只要做到穷举即可。除了深度优先搜索之外,我们还可采用广度优先搜索,而分支限界法则是对解空间进行优先级优先搜索。