(本文参考 OI Wiki )
一、枚举法
枚举的思想是不断地猜测,从可能的集合中一一尝试,然后再判断题目的条件是否成立。
要点:
1)给出解空间:在一定范围内,可能发生的情况是什么?
2)减少枚举的空间:目的是减少不必要的开销
3)选择合适的枚举顺序:根据题目要求,选择最合理的枚举顺序
二、模拟
模拟就是用计算机来模拟题目中要求的操作。
模拟题目通常具有码量大、操作多、思路繁复的特点。
三、递归
递归,在数学和计算机科学中是指在函数的定义中使用函数自身的方法,在计算机科学中还额外指一种通过重复将问题分解为同类的子问题而解决问题的方法。
什么是递归?
递归的基本思想是某个函数直接或者间接地调用自身,这样原问题的求解就转换为了许多性质相同但是规模更小的子问题。求解时只需要关注如何把原问题划分成符合条件的子问题,而不需要过分关注这个子问题是如何被解决的。
递归的例子:
如何给一堆数字排序?答:分成两半,先排左半边再排右半边,最后合并就行了,至于怎么排左边和右边,请重新阅读这句话。
递归代码最重要的两个特征:结束条件和自我调用。自我调用是在解决子问题,而结束条件定义了最简子问题的答案。
int func(传入数值){
if(终止条件) return最小子问题解; //结束条件
return func(缩小规模); //自我调用
}
为什么要写递归?
1、递归结构清晰,可读性强。
2、使用递归能练习分析问题的结构。当发现问题可以被分解成相同结构的小问题时,递归写多了就能敏锐发现这个特点,进而高效解决问题。
递归的缺点:
1)在程序执行中,递归是利用堆栈来实现的。每当进入一个函数调用,栈就会增加一层栈帧,每次函数返回,栈就会减少一层栈帧。而栈不是无限大的,当递归层数过多时,就会造成 栈溢出 的后果。
2)显然有时候递归处理是高效的,比如归并排序;有时候是低效的,因为堆栈会消耗额外空间,而简单的递推不会消耗空间。
递归优化:
主页面:搜索优化 和 记忆化搜索
比较初级的递归实现可能递归次数太多,容易超时。这时需要对递归进行优化。
写递归的要点:
明白一个函数的作用并相信它能完成这个任务,千万不要跳进这个函数里面企图探究更多细节
四、分治
分治算法的核心思想就是“分而治之”。
大概的流程可以分为三步:分解 -> 解决 -> 合并。
1、分解原问题为结构相同的子问题。
2、分解到某个容易求解的边界之后,进行递归求解。
3、将子问题的解合并成原问题的解。
分治法能解决的问题一般有如下特征:
1)问题规模较小:该问题的规模缩小到一定的程度就可以容易地解决。
2)问题可以分解与合并:该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质,利用该问题分解出的子问题的解可以合并为该问题的解。
3)分解的子问题是独立的:该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
注意:(如果各子问题是不独立的,则分治法要重复地解公共的子问题,也就做了许多不必要的工作。此时虽然也可用分治法,但一般用 动态规划 较好。)
区别:
递归与枚举:
递归和枚举的区别在于:枚举是横向 地把问题划分,然后依次求解子问题;而递归是把问题逐级分解,是纵向 的拆分。
递归与分治:
递归是一种编程技巧,一种解决问题的思维方式 ;分治算法很大程度上是基于递归的,解决更具体问题的算法思想。