1. 程式人生 > >貪心選擇、動態規劃、分治及回溯法的理解

貪心選擇、動態規劃、分治及回溯法的理解

1.演算法特徵

分治演算法:

分治演算法特徵:

1)規模如果很小,則很容易解決。//一般問題都能滿足

2)大問題可以分為若干規模小的相同問題。//前提

3)利用子問題的解,可以合併成該問題的解。//關鍵

4)分解出的各個子問題相互獨立,子問題不再包含公共子問題。 //效率高低

上述的第一條特徵是絕大多數問題都可以滿足的,因為問題的計算複雜性一般是隨著問題規模的增加而增加;

第二條特徵是分治法應用的前提,它也是大多數問題可以滿足的,此特徵反映了遞迴思想的應用;

第三條特徵是關鍵,能否利用分治法完全取決於問題是否具有第三條特徵,如果具備了第一條和第二條特徵,而不具備第三條特徵,則可以考慮貪心演算法或動態規劃演算法;

第四條特徵涉及到分治法的效率,如果各個子問題不是獨立的,則分治法要做許多不必要的工作,重複地解公共的子問題。這類問題雖然可以用分治法解決,但用動態規劃演算法解決效率更高。

動態規劃:

1)最優子結構:問題的最優解包含了其子問題的最優解時,稱該問題具有最優子結構性質;
2)重疊子問題:有些子問題被反覆計算多次,並將子問題的解儲存;

拓展動態規劃(Memorization): 與動態規劃一樣,用表格儲存已解決的子問題的答案,不同的是此法採用遞迴方式自頂向下,因此此法的控制結構與直接遞迴方法相同,區別在於表格儲存子問題答案避免了相同子問題重複求解;

特徵:動態規劃任何一個i+1階段都僅僅依賴 i 階段做出的選擇。而與i之前的選擇無關。但是動態規劃不僅求出了當前狀態最優值,而且同時求出了到中間狀態的最優值。
缺點:空間需求大。

貪心演算法

依賴:依賴於當前已經做出的所有選擇。

自頂向下(就是每一步,根據策略得到一個當前最優解。傳遞到下一步,從而保證每一步都是選擇當前最優的。最後得到結果)

進階理解:

分而治之(Divide and Conquer)
這裡只談狹義的D&C,即將問題分成幾個部分,每一部分相互獨立,互不重疊,假定每個部分都可以得到解決來進行遞迴呼叫,合併每一部分的結果。
例如Merge Sort, Quick Sort (Merge Sort的divide容易,但Conquer/Merge複雜,Quick Sort的divide複雜,但Conquer/Merge容易)
動態規劃

(Dynamic Programming)
儘可能不重複計算每個子問題,而是將計算結果儲存下來,以確定後驅問題的解。
與貪心演算法的區別是,會記錄下所有可能通向全域性最優解的區域性解,以便在計算後驅問題時綜合考慮多個前驅問題的解。
貪心演算法(Greedy Algorithm)
只做出當下最優的判斷,並且以此為基礎進行下一步計算。當前判斷最優時,不考慮對全域性/未來的影響,所以所以從全域性來說並不能保證總是最優。
貪心演算法每次更新當前的最優解。如Dijkstra演算法就是貪心演算法的例項之一。
回溯 (Backtracking)
一種暴力(窮舉)的深度優先搜尋法:搜尋,直到節點空間的盡頭,然後再返回到上次的節點,再往其他方向深度搜索。
樹或圖的DFS是回溯的例項之一。

2.演算法選擇

貪心演算法:

①貪心選擇性質:在求解一個問題的過程中,如果再每一個階段的選擇都是當前狀態下的最優選擇,即區域性最優選擇,並且最終能夠求得問題的整體最優解,那麼說明這個問題可以通過貪心選擇來求解,這時就說明此問題具有貪心選擇性質。

②最優子結構性質:當一個問題的最優解包含了這個問題的子問題的最優解時,就說明該問題具有最優子結構。

分治演算法:見1,演算法間的關聯與不同中的①②③④。

動態規劃:

①最優化原理:如果問題的最優解所包含的子問題的解也是最優的,就稱該問題具有最優子結構,即滿足最優化原理

②無後效性:即某階段狀態一旦確定,就不受這個狀態以後決策的影響。也就是說,某狀態以後的過程不會影響以前的狀態,只與當前狀態有關。

有重迭子問題:即子問題之間是不獨立的,一個子問題在下一階段決策中可能被多次使用到。

注:採用動態規劃方法,可以高效地解決許多用貪婪演算法或分而治之演算法無法解決的問題。

但貪心演算法也有它的優勢:構造貪心策略不是很困難,而且貪心策略一旦經過證明成立後,它就是一種高效的演算法。

自底向上與自頂向下

以樹形結構根在上葉節點在下為例,根為頂,葉為底;
自底向上:從已知的初始狀態出發,向外拓展,最終到達目標狀態;形式上前者對應iteration(迭代),利用迴圈將結果存在數組裡,從陣列起始位置向後計算。

int array[n] = {0};
array[1] = 1;
for (int i = 2; i < n; i++)
    array[i] = array[i-1] + array[i-2];

自頂向下:從最終狀態開始,找到可以到達當前狀態的狀態,如果該狀態還沒處理,就先處理該狀態;對應recursion,即利用函式呼叫自身實現。

int Fibonacci(int n)
{
    if(n == 0)
        return 0;
    if(n == 1)
        return 1;
    return Fibonacci(n-1) + Fibonacci(n-2);
}

如果不儲存上一個狀態的解,則為遞迴,否則就是DP(動態規劃),比如,Fibonacci(n-1)與Fibonacci(n-2)包含很多重複的子問題,所以DP效率更高。如果用一個全域性陣列,將子問題的解儲存到陣列的對應位置,在重複計算的時候直接讀取計算結果,那麼就是DP的解法。
一般地,動態規劃特指迭代形式自底向上的動態程式設計;將自頂向下,遞迴形式,在遞迴過程中用雜湊表記錄中間計算結果的DP,稱作Memorization
動態規劃核心在於,如果在一個問題的解決方案中,子問題被重複計算,那麼就可以利用記錄中間結果,達到用空間換取時間的目的。
動態規劃常用的模型:
線性模型;最大公共子序列
樹形模型;最優二叉搜尋樹
區間問題;
揹包問題;0-1揹包
參考連結:https://wdxtub.com/interview/14520597062776.html