第02篇 ACM/ICPC競賽之算法策略 3

ACM/ICPC競賽其實就是算法設計和編碼的競賽,熟悉各種常用算法和算法設計策略並能靈活運用是非常必要的。

這裏對幾種在競賽中經常用到的算法設計策略做一簡單的介紹。

1、窮舉法

窮舉法是最基本的算法設計策略,其思想是列舉出問題所有的可能解,逐一進行判別,找出滿足條件的解。

窮舉法的運用關鍵在於解決兩個問題:

如何列舉所有的可能解;

如何判別可能解是否滿足條件;

在運用窮舉法時,容易出現的問題是可能解過多,導致算法效率很低,這就需要對列舉可能解的方法進行優化。

以題1041--純素數問題爲例,從1000到9999都可以看作是可能解,可以通過對所有這些可能解逐一進行判別,找出其中的純素數,但只要稍作分析,就會發現其實可以大幅度地降低可能解的範圍。根據題意易知,個位只可能是3、5、7,再根據題意可知,可以在3、5、7的基礎上,先找出所有的二位純素數,再在二位純素數基礎上找出三位純素數,最後在三位純素數的基礎上找出所有的四位純素數。

2、分治法

分治法也是應用非常廣泛的一種算法設計策略,其思想是將問題分解爲若干子問題,從而可以遞歸地求解各子問題,再綜合出問題的解。

分治法的運用關鍵在於解決三個問題:

確定分治規則,即如何分解問題。

確定終結條件,即問題分解到什麼狀態時可以直接求解。

確定歸納方法,即如何由子問題的解得到原問題的解。這一步並不總是需要的,因爲對某些問題來說,並不需要對子問題的解進行復雜的歸納。

我們熟知的如漢諾塔問題、折半查找算法、快速排序算法等都是分治法運用的典型案例。

以題1045--Square Coins爲例,先對題意進行分析,可設一個函數f(m, n)等於用面值不超過n2的貨幣構成總值爲m的方案數,則容易推導出:

f(m, n) = f(m-0*n*n, n-1)+f(m-1*n*n, n-1)+f(m-2*n*n, n-1)+...+f(m-k*n*n, n-1)

這裏的k是幣值爲n2的貨幣最多可以用多少枚,即k=m/(n*n)。

也很容易分析出,f(m, 1) = f(1, n) = 1

對於這樣的題目,一旦分析出了遞推公式,程序就非常好寫了。所以在動手開始寫程序之前,分析工作做得越徹底,邏輯描述越準確、簡潔,寫起程序來就會越容易。

3、動態規劃法

動態規劃法多用來計算最優問題,動態規劃法與分治法的基本思想是一致的,但處理的手法不同。動態規劃法在運用時,要先對問題的分治規律進行分析,找出終結子問題,以及子問題向父問題歸納的規則,而算法則直接從終結子問題開始求解,逐層向上歸納,直到歸納出原問題的解。

動態規劃法多用於在分治過程中,子問題可能重複出現的情況,在這種情況下,如果按照常規的分治法,自上向下分治求解,則重複出現的子問題就會被重複地求解,從而增大了冗餘計算量,降低了求解效率。而採用動態規劃法,自底向上求解,每個子問題只計算一次,就可以避免這種重複的求解了。

動態規劃法還有另外一種實現形式,即備忘錄法。備忘錄的基本思想是設立一個稱爲備忘錄的容器,記錄已經求得解的子問題及其解。仍然採用與分治法相同的自上向下分治求解的策略,只是對每一個分解出的子問題,先在備忘錄中查找該子問題,如果備忘錄中已經存在該子問題,則不須再求解,可以從備忘錄中直接得到解,否則,對子問題遞歸求解,且每求得一個子問題的解,都將子問題及解存入備忘錄中。

例如,在題1045--Square Coins中,可以採用分治法求解,也可以採用動態規劃法求解,即從f(m, 1)和f(1, n)出發,逐層向上計算,直到求得f(m, n)。

在競賽中,動態規劃和備忘錄的思想還可以有另一種用法。有些題目中的可能問題數是有限的,而在一次運行中可能需要計算多個測試用例,可以採用備忘錄的方法,預先將所有的問題的解記錄下來,然後輸入一個測試用例,就查備忘錄,直接找到答案輸出。這在各問題之間存在父子關係的情況下,會更有效。例如,在題1045--Square Coins中,題目中已經指出了最大的目標幣值不超過300,也就是說問題數只有300個,而且各問題的計算中存在重疊的子問題,可以採用動態規劃法,將所有問題的解先全部計算出來,再依次輸入測試用例數據,並直接輸出答案。

4、回溯法

回溯法是基於問題狀態樹搜索的求解法,其可適用範圍很廣。從某種角度上說,可以把回溯法看作是優化了的窮舉法。回溯法的基本思想是逐步構造問題的可能解,一邊構造,一邊用約束條件進行判別,一旦發現已經不可能構造出滿足條件的解了,則退回上一步構造過程,重新進行構造。這個退回的過程,就稱之爲“回溯”。

回溯法在運用時,要解決的關鍵問題在於:

如何描述局部解。

如何擴展局部解和回溯局部解。

如何判別局部解。

回溯法的經典案例也很多,例如全排列問題、N後問題等。

5、貪心法

貪心法也是求解最優問題的常用算法策略,利用貪心法策略所設計的算法,通常效率較高,算法簡單。貪心法的基本思想是對問題做出目前看來最好的選擇,即貪心選擇,並使問題轉化爲規模更小的子問題。如此迭代,直到子問題可以直接求解。

基於貪心法的經典算法例如:哈夫曼算法、最小生成樹算法、最短路徑算法等。

但是,貪心法的運用是有條件的,必須能夠證明貪心選擇能夠導出最優解,且轉化出的子問題與原問題是同性質的問題,才能使用貪心法求解。

一個比較經典的貪心法求解的問題就是找硬幣問題:有1、2、5、10、20、50、100七種面值的硬幣,要支付指定的金額,問怎麼支付所用的硬幣個數最少。這是一個非常日常化的問題,憑直覺我們會想到,儘可能先用大面值的硬幣,這就是“貪心選擇”,而在這個問題上,這個貪心選擇也是正確的。

6、限界剪枝法

限界剪枝法是求解較複雜最優問題的一種算法策略,與回溯法類似的是,限界剪枝法也是在問題狀態空間樹上進行搜索,但回溯法是搜索一般解,而限界剪枝法則是搜索最優解。限界剪枝法的基本思想是通過找出權值函數的上下界函數,以下界函數來指導搜索的方向,以上界函數來幫助剪除一些不可能含有最優解的分枝。

關於算法和算法策略的討論是一個非常龐大的話題,幾乎每個問題點都能擴展出一大堆可討論的內容和案例。我實在不知道該怎樣用簡短的幾篇文字就能夠把這個話題說透,這裏只能蜻蜓點水地對競賽中經常用到的幾種策略做一極爲簡略的介紹。

也許我們可以在以後的文章中,針對具體的題目進行算法和策略的分析,效果可能會更好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章