1. 程式人生 > >貪心演算法和動態規劃的個人理解

貪心演算法和動態規劃的個人理解

最近通過各個公司的筆試題發現,好多程式設計題都是貪心演算法和動態規劃演算法。這兩個也容易混淆,在網上也看了好多這兩種演算法的解析,通過這篇文章寫下自己對這兩種演算法的理解。

使用動態規劃的最大特性是原問題的最優解必須包含子問題的最優解。下面通過一個例子解釋:求圖的最短路徑是解釋動態規劃最容易的問題。下面是一個多段圖:

按照貪心演算法的思路解決問題可以得到:v1到v2:1到2最短,所以選擇2,總長s為2。v2到v3:2到5最短,所以選擇5,總長為2+6=8。v3到v4:5到10最短,所以選擇10,總長為8+6=14。v4到v5:10到11,總長為14+10=24。所以最短路徑為1,2,5,10,11

但是這明顯不是最短路徑,所以通過貪心演算法思路解決的話是錯誤的。

通過動態規劃演算法解決多段圖最短路徑規劃問題的思想是,我們要計算每一個子段圖到下一個子段圖的所有可能的連線情況,並保留每種可能選擇中最短的那個連線。

假設我們從終點反推到起點,則具體的操作可以是這樣的:

v5到v4有三種路可選:11到8,11到9,11到10。記為{11,8}=6,{11,9}=5,{11,,10}=10.

v4到v3:8到5,8到6,9到6,10到5,10到6,10到7。記最短路徑是:8到6,這是最短路徑為11到8到6

v3到v2:6到2,6到3,6到4,記最短路徑為:6到3,這是最短路徑為11到8到6到3.

v2到v1:3到1,這時最短路徑為11到8到6到3到1

從而求出多段圖的最短路徑為{1,3,6,8,11}

從上述推演過程中可以得知動態規劃的演算法每次都保留所有可能的最優解,那總體最優肯定包含著區域性最優。而構成整體最優不一定是由當前最優解構成的。因此需要對全部解決方案進行標記。

一般我們採用動態規劃演算法解決問題時,首先要明確問題是否具有最優子結構,然後找出問題最優子結構的遞推關係式,例如多段圖最短路徑規劃問題中的遞推公式可表述如下:

cost(i,j) = min{ c(j,l) + cost(i+1,l) },l∈Vi+1,(j,l)∈E,E為多段圖的邊集,Vi+1為第i+1段中的點集

其中cost(i,j)表示的是第i段的第j個頂點到終點t的路徑長度的最短值,l為第i+1段中的某個頂點,然後c(j,l)為頂點j到l之間的連線權值,即距離值。

貪心演算法的解決思路就是:將問題分解為一個個子問題,找到子問題的最優解,就能得到原問題的最優解。

  1. 要求解的問題是一個最優化問題;
  2. 這個問題的解可以用n元組表示;
  3. 該問題滿足最優子結構特性;
  4. 可以找到最優量度標準,並可以證明該最優量度標準能導致一個整體最優解;

適用場景:

1、單源最短路經問題 
2、最小生成樹問題 
3、可任意分割的揹包問題。如果不可以任意分割,就需要用動態規劃求解。 

貪心演算法的原型:

SolutionType greedy(int[] a){

    // 一開始結果集為空
    SolutionType solution = {};
    // 進行n步選值
    for ( int i=0; i<n; i++ ) {
        // 選出當前區域性最優解x
        x = select(a);
        // 判斷x是否滿足約束條件,若不滿足則繼續選
        while( !isFeasible(x) ){
            x = select(a);
        }
        // 將當前最優解新增至結果集中
        solution.add(x);
    }
}