深搜的剪枝技巧
首先是深搜的模板:
int ans = 最坏情况, now; // now为当前答案 void dfs(传入数值) { if (到达目的地) ans = 从当前解与已有解中选最优; for (遍历所有可能性) if (可行) { 进行操作; dfs(缩小规模); 撤回操作; } }
1.剪枝的概念:
实际上,对于搜索,其实就是一棵树:
(树丑,莫要介意)
那么对于没有剪枝的dfs,需要搜索整棵树,而剪枝,就是将其中一部分枝干减掉,使时间复杂度降低。
2.
剪枝的原则:三个原则:正确性(这是剪枝优化的前提),准确性,高效性;
3.深搜的优化技巧:
- 优化搜索顺序
- 排除等效冗杂
- 记忆化
- 最优性剪枝
- 可行性剪枝
1.优化搜索顺序:不同的搜索顺序会产生不同的搜索树形态,其规模大小也相差甚远
2.排除等效冗杂:在搜索中,若我们发现沿某几条线路所达到的效果是一样的,那么我们可以只搜索其中的一条
3.记忆化:是啥?:
- 不依赖任何 外部变量
- 答案以返回值的形式存在,而不能以参数的形式存在(就是不能将 dfs 定义成这里面的 nowans 不符合要求)。
- 对于相同一组参数,dfs 返回值总是相同的
模板:
int g[MAXN]; //定义记忆化数组 int ans = 最坏情况, now; void dfs f(传入数值) { if (g[规模] != 无效数值) return; //或记录解,视情况而定 if (到达目的地) ans = 从当前解与已有解中选最优; //输出解,视情况而定 for (遍历所有可能性) if (可行) { 进行操作; dfs(缩小规模); 撤回操作; } } int main() { ... memset(g, 无效数值, sizeof(g)); //初始化记忆化数组 ... }
4.最优性剪枝:在搜索中导致运行慢的原因还有一种,就是在当前解已经比已有解差时仍然在搜索,那么我们只需要判断一下当前解是否已经差于已有解。
模板:
int ans = 最坏情况, now; void dfs(传入数值) { if (now比ans的答案还要差) return; if (到达目的地) ans = 从当前解与已有解中选最优; for (遍历所有可能性) if (可行) { 进行操作; dfs(缩小规模); 撤回操作; } }
5.可行性剪枝:
在搜索中如果当前解已经不可用了还运行,也是在搜索中导致运行慢的原因。
int ans = 最坏情况, now; void dfs(传入数值) { if (当前解已不可用) return; if (到达目的地) ans = 从当前解与已有解中选最优; for (遍历所有可能性) if (可行) { 进行操作; dfs(缩小规模); 撤回操作; } }
下面我们来看一个简单的剪枝的例题:【洛谷p1025】数的划分
下面我们来看一个超多剪枝的例题:【洛谷UVA307】小木棍Sticks