1. 程式人生 > >ACM搜尋演算法總結

ACM搜尋演算法總結

轉自:http://blog.csdn.net/shenmen123456/article/details/6695499 在此感謝~

搜尋是ACM競賽中的常見演算法,本文的主要內容就是分析它的 特點,以及在實際問題中如何合理的選擇搜尋方法,提高效率。文章的第一部分首先分析了各種基本的搜尋及其各自的特點。第二部分在基本搜尋方法的基礎上提出 一些更高階的搜尋,提高搜尋的效率。第三部分將搜尋和動態規劃結合,高效地解決實際問題,體現搜尋的廣泛應用性。第四部分總結全文。


文章在分析各種搜尋的同時,分析了我們在解題中應該怎樣合理利用它,理論結合實際,對我們的解題實踐有一定的指導意義。

【 Abstract 】 Search is a algorithm which is often seen in ACM/ICPC .The main idea of this article is to analysis its specially characterist and how to choose search method reasonably for the increasing efficiency in practical problems.


The first section analysis every basic search method and each specially characterist. The second section bring up some advanced search methods to increase the efficiency.The third section is to combine the search method and dynamic programing method to solve practical problem efficiencily indicating that search methods have weed applicability.The fourth section is to sum up the article and prospect the development of search.


提起搜尋,大家都不會陌生。它的應用是十分廣泛的,比如目前internet上的搜尋引擎,WINDOWS XP作業系統中的檔案搜尋。同時,搜尋是程式設計解題的一種重要的手段,在競賽中,我們有時會碰到一些題目,它們既不能通過建立數學模型解決,又沒有現成演算法 可以套用,或者非遍歷所有狀況才可以得出正確結果。這時,我們就必須採用搜尋演算法來解決問題。幾乎每次ACM競賽都要考察到這方面的內容。因此,如何更深 入地瞭解搜尋,從而更為有效地運用這個解題的有力武器,是一個值得深入研究的問題。要掌握搜尋的應用技巧,就要了解它的分類及其各方面的特點。

第一部分 基本的搜尋演算法
一、回溯演算法
回溯演算法是所有搜尋演算法中最為基本的一種演算法,其採用了一種“走不通就掉頭”思想作為其控制結構,其相當於採用了先根遍歷的方法來構造解答樹,可用於找解或所有解以及最優解。


評價:回溯演算法對空間的消耗較少,當其與分枝定界法一起使用時,對於所求解在解答樹中層較深的問題 有較好的效果。但應避免在後繼節點可能與前繼節點相同的問題中使用,以免產生迴圈。

二、深度搜索與廣度搜索
深度搜索與廣度搜索的控制結構和產生系統很相似,唯一的區別在於對擴充套件節點選取上。由於其保留了所有的前繼節點,所以在產生後繼節點時可以去掉一部分重複 的節點,從而提高了搜尋效率。這兩種演算法每次都擴充套件一個節點的所有子節點,而不同的是,深度搜索下一次擴充套件的是本次擴展出來的子節點中的一個,而廣度搜索 擴充套件的則是本次擴充套件的節點的兄弟節點。在具體實現上為了提高效率,所以採用了不同的資料結構.

評價:廣度搜索是求解最優解的一種較好的方法,在後面將會對其進行進一步的優化。而深度搜索多用於只要求解,並且解答樹中的重複節點較多並且重複較難判斷時使用,但往往可以用A*或回溯演算法代替。

第二部分 搜尋演算法的優化(一)
一、雙向廣度搜索
廣度搜索雖然可以得到最優解,但是其空間消耗增長太快。但如果從正反兩個方向進行廣度搜索,理想情況下可以減少二分之一的搜尋量,從而提高搜尋速度。

二、分支定界
分支定界實際上是A*演算法的一種雛形,其對於每個擴展出來的節點給出一個預期值,如果這個預期值不如當前已經搜尋出來的結果好的話,則將這個節點(包括其子節點)從解答樹中刪去,從而達到加快搜索速度的目的。

三、A*演算法
A*演算法中更一般的引入了一個估價函式f,其定義為f=g+h。其中g為到達當前節點的耗費,而h表示對從當前節點到達目標節點的耗費的估計。其必須滿足兩個條件:

1. h必須小於等於實際的從當前節點到達目標節點的最小耗費h*。
2. f必須保持單調遞增。

A*演算法的控制結構與廣度搜索的十分類似,只是每次擴充套件的都是當前待擴充套件節點中f值最小的一個,如果擴展出來的節點與已擴充套件的節點重複,則刪去這個節點。如果與待擴充套件節點重複,如果這個節點的估價函式值較小,則用其代替原待擴充套件節點。

對A*演算法的改進–分階段A*. 當A*演算法出現數據溢位時,從待擴充套件節點中取出若干個估價函式值較小的節點,然後放棄其餘的待擴充套件節點,從而可以使搜尋進一步的進行下去。

四、A*演算法與回溯的結合(IDA*)
這是A*演算法的一個變形,很好綜合了A*演算法的人工智慧性和回溯法對空間的消耗較少的優點,在一些規模很大的搜尋問題中會起意想不到的效果。它的具體名稱 是 Iterative Deepening A*, 1985年由Korf提出。該演算法的最初目的是為了利用深度搜索的優勢解決廣度A*的空間問題,其代價是會產生重複搜尋。歸納一下,IDA*的基本思路 是:首先將初始狀態結點的H值設為閾值maxH,然後進行深度優先搜尋,搜尋過程中忽略所有H值大於maxH的結點;如果沒有找到解,則加大閾值 maxH,再重複上述搜尋,直到找到一個解。在保證H值的計算滿足A*演算法的要求下,可以證明找到的這個解一定是最優解。在程式實現上,IDA* 要比 A* 方便,因為不需要儲存結點,不需要判重複,也不需要根據 H值對結點排序,佔用空間小。

下面,以一個具體的例項來分析比較上述幾種搜尋演算法的效率等問題。

在scu online judge(http://cs.scu.edu.cn/acm)上有這麼一道題目:這就是古老而又經典的15數碼難題:在4*4的棋盤上,擺有15個棋 子,每個棋子分別標有1-15的某一個數字。棋盤中有一個空格,空格周圍的棋子可以移到空格中。現給出初始狀態和目標狀態,要求找到一種移動步驟最少的方 法。

看到這個題目,會發覺幾乎每個搜尋演算法都可以解這個問題。而事實確實如此。

首先考慮深度優先搜尋,它會遍歷這棵解答樹。這棵解答樹最多可達16!個節點,深度優先搜尋必須全部遍歷後,才能從所有解中選出最小的一個做為答案,其代價是非常巨大的。

其次考慮廣度深度優先搜尋,這不失為一個好辦法。因為廣度優先搜尋的層次遍歷解答樹的特點,一旦搜尋到一個目標節點,那麼這時的深度一定是最優解,而不必 象深度優先搜尋那樣繼續搜尋目標節點,最後比較才能得出最優解。該搜尋方法在這道題目上會遇見致命的問題:廣度深度優先搜尋是一種盲目的搜尋,深度比較大 的測試資料會產生大量的無用的節點,同時消耗很多時間在重複節點的判斷上。

為了減少重複的節點,加入人工智慧性,馬上可以想到用A*演算法。經過分析發現,該方法對避免產生大量的無用的節點起到了一定的效果,但是會花97%以上的 時間去判斷新產生節點是否與已擴充套件的和待擴充套件的節點重複。看來如何提高判重的速度成為該題目的關鍵。解決這個問題有很多辦法,比如引入雜湊表,對已擴充套件的 和待擴充套件的節點採用雜湊表儲存,減少判重的代價,或者對已擴充套件的和待擴充套件的節點採用桶排序,也可以減少判重的代價。我們現在來嘗試一下用 IDA*演算法。該演算法有個值得注意的地方:對估計函式的選取。如果選用當前狀態每個位置上與目標狀態每個位置上相同節點的數目加當前狀態的深度作為估計函 數,由於當前狀態每個位置上與目標狀態每個位置上相同節點的數目這個值一般較小,不能明顯顯示各個狀態之間的差別,執行過程中會產生大量的無用的節點,同 樣會使效率很低,不能在60s以內完成計算。比較優化的一個辦法是選用由於當前狀態每個位置上的數字偏離目標節點該數字的位置的距離加當前狀態的深度作為 估計函式。這個估計函式的選取沒有統一的標準,找到合適的該函式並不容易,但是可以大致按照這個原則:在一定範圍內加大各個狀態啟發函式值的差別。

實踐證明,該方法用廣泛的通用性,在很多情況下可以替換一般的深度優先搜尋和廣度優先搜尋。

第三部分 搜尋演算法的優化(二)
該部分將談到搜尋與其他演算法的結合。再看scu online judge的一道題目: 給定一個8 * 8的國際象棋棋盤。給出棋盤上任意兩個位置的座標,問馬最少幾步可以從一個位置跳到另外一個位置。(POJ1915類似)

該題目同樣是求最優解,如果用一般的深度優先搜尋是很容易超時的。如果用廣度優先搜尋,會消耗大量的記憶體,而且效率是很低的。這裡,我們將嘗試用深度優先搜尋加動態規劃的演算法解決該問題。

將該棋盤做為儲存狀態的矩陣。每個矩陣元素的值是該位置到初始位置最少需要的步數。初始位置的元素值為0。其他位置的元素初始化為一個很大的正整數。首先 從初始位置開始深度優先搜尋,例如某次從(i1,j1)到達位置(i2,j2),如果(i2,j2)處的值大於(i1,j1)的值加1,則(i2,j2) 處的值更新為(i1,j1)+ 1,表示從(i1,j1) 跳到(i2,j2)比從其他地方跳到(i2,j2)更優,不斷的進行這個過程,直到不能進行下去位置,那麼最後的目標位置的值就是解。這就是一個動態規劃 的思想,每個位置的最優解都是由其他能夠一次跳到這個位置的位置的值決定的,而且是它們中的最小值。同時,該動態規劃又藉助深度優先搜尋這個工具,完成對 每個位置的值的重新整理,可以算是一個比較經典的深度優先搜尋和動態規劃的結合。該問題還需要注意一個剪枝的問題,從起始位置到目標位置的最大步數是多少?經 過計算,最大值是6。所以一旦某個位置的值是6了,就不必再將它去重新整理另外的位置,從而剪去了對很多不必要子樹的搜尋,大大提高了效率。

第四部分結語
本文的主要的篇幅講的都是理論,但是根本的目的還是指導實踐。搜尋,據我認為,是當今ACM競賽中最常規、也最能體現解題者水平的一類解題方法。 “紙上得來終覺淺,絕知此事要躬行。”要想真正領悟、理解各種搜尋的思想,掌握搜尋的解題技巧,還需要在實踐中不斷地挖掘、探索。實踐得多了,也就能體會 到漸入佳境之妙了。演算法的優化是無窮盡的。

相關推薦

ACM搜尋演算法總結

轉自:http://blog.csdn.net/shenmen123456/article/details/6695499 在此感謝~ 搜尋是ACM競賽中的常見演算法,本文的主要內容就是分析它的 特點,以及在實際問題中如何合理的選擇搜尋方法,提高效率。文章的第一部分首先分析了各種基本的搜尋及其各自的特點。第

字串搜尋演算法總結

因為在網上搜尋hash演算法的知識,無意中又找到一些字串搜尋演算法。 由於之前已經學習過一些搜尋演算法,覺得應該可以歸為一類。因此就寫一篇文章來記錄下學習的過程。 問題: 在一長字串中找出其是否包含某子字串。 首先當然還是簡單演算法,通過遍歷來檢索所有的可能:  publ

演算法總結-二叉樹的深度優先搜尋

1 遍歷的問題 二叉樹的前序遍歷 http://www.lintcode.com/zh-cn/problem/binary-tree-preorder-traversal/ 二叉樹的中序遍歷 http://www.lintcode.com/zh-cn/problem/b

演算法總結之深度優先搜尋

1 組合搜尋問題 子集 http://www.lintcode.com/zh-cn/problem/subsets/ 帶重複元素的子集 http://www.lintcode.com/zh-cn/problem/subsets-ii/ 數字組合 http://www

ACM搜尋演算法專題(1)——24點

題目描述:給定4個數字,判定這4個數字是否可以通過運算得到結果24。運算操作包括:加、減、乘、除,允許變換數字的順序,允許使用括號改變運算順序。     即:判定是否存在一種通過在下面的圓圈中新增運算子以及新增括號的方式使得等式成立:             a ○ b ○

2017年山東省ACM省賽總結

來看 其他 偉大的 之間 。。 bug 暑假 引導 問心無愧 2017年山東省ACM省賽總結

Java 常用排序演算法總結

氣泡排序:  /*冒泡演算法*/ public class BubbleSort { public static void bubble_sort(int[] arr){ int temp; for(int i = 0; i < arr

2018.10.31 遞迴演算法總結

///十進位制轉二進位制 void dectobin( int n ) { if(n==0) return; dectobin(n/2); printf("%d",n%2); } ///遞迴求斐波那契數列 int fib(int n) { if(n==1 ||

匈牙利演算法總結

二分圖: 定義:如果一個圖的所有頂點可以被分為X和Y兩個集合,並且所有邊的兩個頂點恰好一個屬於集合X,另一個屬於集合Y,即每個集合內的頂點沒有邊相連,那麼此圖就是二分圖。 很多問題都可以轉化為二分圖匹配模型來計算。二分圖有如下幾種常見變形: (1)最小頂點覆蓋 選取最少的點(X或

博弈論的演算法總結

  開頭先囉嗦一句:想學好博弈,必然要花費很多的時間,深入學習,不要存在一知半解,應該是一看到題目,就想到博弈的型別。 以及,想不斷重複不斷重複,做大量各大oj網站的題目,最後吃透它。 博弈:   博弈論又被稱為對策論(Game Theory),既是現代數學的一個新分支,也是運籌學的一個重要學科。 博

12、【演算法】查詢演算法總結

一、順序查詢 1、定義     順序查詢屬於無序查詢,從資料結構的一端開始,順序掃描,依次將掃描到的節點關鍵字與給定值K相比,若相等,則表示查詢成功,若掃描結束,仍未找到關鍵字與給定值K相等,則表示查詢失敗。 時間複雜度分析     查詢成功時:平均查詢長度為(N+1)/2   

11、【演算法】排序演算法總結

常見排序演算法總結 一、氣泡排序 1、定義     氣泡排序是一種比較簡單的排序演算法,它會遍歷若干次要排序的數列,每次便利時,它都會從前往後依次的比較兩個相鄰的數的大小;如果前者比後者大,則交換它們的位置。     這樣一次遍歷之後,最大的元素就在數列的末尾了。採用相同的方法在

對於深度優先搜尋演算法的理解

1. dfs嘗試走遍可能的路線 所以一看到題目確定要使用dfs來解決的時候首先要在紙上畫出簡單的例子的樹,然後進行簡單的模擬呼叫dfs的過程,通過這顆簡單的樹可以清楚地瞭解呼叫的情況,從簡單的例子和自己的總結中看出有多少個平行的狀態,並且每個平行狀態退回來需要怎麼樣處理 其中確定了使用dfs處理

【轉載】黃金比例搜尋演算法(Golden Section Search)的實現

出處: https://www.codelast.com/%E5%8E%9F%E5%88%9B-%E9%BB%84%E9%87%91%E6%AF%94%E4%BE%8B%E6%90%9C%E7%B4%A2%E7%AE%97%E6%B3%95%EF%BC%88gold   黃金比

【極客時間】資料結構與演算法總結

【極客時間】資料結構與演算法總結: 02| 資料結構是為演算法服務的,演算法要作用在特定的資料結構之上。 20個最常用的最基礎的資料結構與演算法: 10個數據結構:陣列、連結串列、棧、佇列、散列表、二叉樹、堆、跳錶、圖、Trie樹 10個演算法:遞迴、排序、二分

改寫二分搜尋演算法

一、實踐題目 改寫二分搜尋演算法 二、問題描述 設a[0:n-1]是已排好序的陣列,請改寫二分搜尋演算法,使得當x不在陣列中時,返回小於x的最大元素位置i和大於x的最小元素位置j。當搜尋元素在陣列中時,i和j相同,均為x在陣列中的位置。 輸入格式: 輸入有兩行:

改寫二分搜尋演算法及對於問題的理解

1、實踐題目:  改寫二分搜尋演算法  2、問題描述:  設陣列a[0:n-1]已排好序,輸入一個整數x。  ①當x不在陣列中時,返回小於x的最大元素位置i和大於x的最小元素位置j。  ②當x在陣列中時,i和j相同,均是x在陣列中的位置。  輸入:第一行是n值和x值,第二行是n個不相同的

【DFS】不撞南牆不回頭—深度優先搜尋演算法[Deep First Search]

今天上午聽到,那個非常6+1的李詠先生因癌症去世 DFS演算法的基本模型 深度下,不撞南牆不回頭,就是一直往後找,知道沒有路了,向後返回。 想起一首民謠,《可能否》--木小雅 https://music.163.com/#/song?id=569214126 現在可能也

演算法設計與計算(改寫二分搜尋演算法)(教材2-3)

二分搜尋 設a[0:n-1]是一個已排好序的陣列。請改寫二分搜尋演算法,使得當搜尋元素x不在陣列中時,返回小於x的最大元素的位置I和大於x的最大元素位置j public static int binarySearch(int []a,int x,int n) {int left=0; int r

青島大學生ACM社團比賽總結---9/26

                                        &nb