搜索是OI beginners 的必修课,也是在各类比赛中十分通用的技巧。已近三年辽宁省的NOIP 成绩为例,我们假设一名选手掌握了基本的搜索和模拟算法,那么TA的成绩将如下所示(均达到辽宁省省一线):
2010: 100 + 30 + 30 + 50 = 210
2009: 100 + 50 + 30 + 40 = 220
2008: 100 + 100 + 30 + 10 = 240
下面就简单谈谈各种常见的搜索并另附了一些可供练习的好题。
一 深度优先搜索
深度优先搜索常用于求可行解、解的总数或者非深度最优的最优解。它可以轻松地用递归实现,当然可能也有人热衷于非递归形式。递归框架如下:
二 广度优先搜索
广度优先搜索常用于求深度最优的最优解。它一般用队列来实现(如果为了节省空间可以选择取模的方式使用循环队列,如最短路的SPFA算法)。其框架如下:
三 判重问题
搜索过程中常常遇到搜索回同一种情况的时候,就像你可以乘坐154从育才北校去南校,也可以打车过去,总之到那之后你该干嘛干嘛!如果搜索到这样一种情况,你即便再往下搜也只是重复了昨天的故事,全是无用功。这时,你就需要想办法判断你是是否回到了过去。我们通常开一个布尔数组来判重。
四 迭代加深搜索
我很喜欢这个迭代加深,我认为它平衡了深搜和广搜的优缺点。它既像BFS那样高效又像DFS那样容易实现。迭代加深尤其适用于记录结点信息比较麻烦的求最深度优解的题目。它得到的第一个可行解必然最优。其框架如下:
可能有人有疑问:你不刚说完无用功的事儿么!这么来回重新搜索不是一堆无用功么!在这里我解释一下:如果你把搜索过程想象成在一棵树上的遍历,那么如果一棵树比另一棵树多一层那么你要搜索的数据量就是翻了一番。
五 搜索的优化
在做搜索题目时,我会反问自己这么几个问题,也是通常的优化思路。在做最后提到的那些题时可以体会到这些问题。
1. 以什么作为搜索对象?
2. 可否进行对解的预处理(动规、随机化……)?
3. 采用什么搜索顺序?
4. 能否忽略明显得不到合理解的结点?(可行性剪枝)
5. 能否忽略明显得不到最优解的结点?(最优化剪枝)
六 抓住比赛的BUG
比赛和考试不同于联系和学习,我们不得不承认功利至上,所以要掌握比赛的技巧。
首先,我来谈谈打表。它常常用于函数类询问,比如:n皇后解的总数、NOIP2008的火柴棒等式……实际操作就是在比赛时跑出所有输入对应的解后,建立常量数组。
其次是卡时技巧。方法是初始化计数器为0,在每次访问结点是计数器加1即可,当达到(时限 * 9 * 10 ^ 9)时,输出当前最优解、当前解总数或者无解时的特判。
最后说一下算法设计效率。我们期望的是得分效率高。记住,2个60分算法优于1个100分算法!也就是说,满分算法不一定是完满算法!
七 推荐题目
名称 | 考察点 | 来源 | 提交 |
生日蛋糕 | 强力剪枝 | NOI 1999 | POJ 1190 |
木棒问题 | 强力剪枝+搜索顺序 | ACM 中欧 | POJ 1011 |
奶牛加密术 | 强力剪枝+搜索顺序 | USACO | USACO 4.1.4 |
质数方阵 | 强力剪枝 | IOI 94 | USACO 4.3.2 |
靶心数独 | 搜索顺序 | NOIP 2009 | RQNOJ 521 |
Betsy的旅行 | 强力剪枝 | USACO | USACO 5.4.4 |
拼球问题 | 细心搜索 | CEOI 1998 | POJ 1725 |
BLOXORZ | 细心搜索 | POJ 07/08/05 月赛 | POJ 3322 |
八数码问题 | 康托判重 | IOI 96 | USACO 3.2.5 |
汽车问题 | 搜索顺序 | IOI 94 | POJ 1167 |
骑士聚会 | 强力剪枝+预处理 | IOI 98 | USACO 3.3.3 |
13皇后 | 对称判重 | 经典问题 | USACO 1.5.4 |
N皇后可行解 | 启发式修补 | 经典问题 | POJ 3239 |
十五数码问题 | 启发函数 | 经典问题 | POJ 1077 |