搜索是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 |