1. 程式人生 > >演算法之動態規劃(爬樓梯)

演算法之動態規劃(爬樓梯)

動態規劃的概念

通過把原問題分解為相對簡單的子問題的方式求解複雜問題的方法。動態規劃常常適用於有重疊子問題和最優子結構性質的問題。

動態規劃的基本思想

若要解一個給定問題,我們需要解其不同部分(即子問題),再合併子問題的解以得出原問題的解。 通常許多子問題非常相似,為此動態規劃法試圖僅僅解決每個子問題一次,從而減少計算量: 一旦某個給定子問題的解已經算出,則將其記憶化儲存,以便下次需要同一個子問題解之時直接查表。 這種做法在重複子問題的數目關於輸入的規模呈指數增長時特別有用。

動態規劃三要素

1>最優子結構
2>邊界
3>狀態轉移公式

動態規劃例子

題目:

    有一座高度是10級臺階的樓梯,從下往上走,每跨一步只能向上1級或者2級臺階。要求用程式來求出一共有多少種走法。

    比如,每次走1級臺階,一共走10步,這是其中一種走法。我們可以簡寫成 1,1,1,1,1,1,1,1,1,1。

思路分析:
首先,我們先想一個問題,如果只差最後一步有幾種到達10層的方法
9 + 1
8 + 2
一種是從8層去十層,一種是九層到十層。
那麼F(10) = F(9)+F(8);
遞迴過去
F(9) = F(8)+F(7)….
F(9),F(8)就是F(10)的最優子結構
F(1) = 1 ,F(2) = 2就是邊界
F(n) = F(n-1)+F(n-2)就是狀態轉移方程


所以得到第一種寫法:

public static int getways(int n) {
        if(n < 1) {
            return 0;
        }
        if(n == 1) {
            return 1;
        }
        if(n == 2) {
            return 2;
        }
        return getways(n-1) + getways(n-2);
    }

但是這樣的遞迴關係有重複的步驟。
可以用一個HashMap來記重
於是有了第二種寫法:

public static int getways(int n,HashMap<Integer,Integer> map) {
        if(n < 1) {
            return 0;
        }
        if(n == 1) {
            return 1;
        }
        if(n == 2) {
            return 2;
        }
        if(map.containsKey(n)) {
            return map.get(n);
        }else {
            int value = getways(n-1) + getways(n-2);
            map.put(n, value);
            return value;
        }
    }

然後思路反轉,既然能遞迴,那麼我們能不能從F(1)迭代到F(10)呢?
第三種方法:

public static int getWays(int n) {
        if(n < 1) {
            return 0;
        }
        if(n == 1) {
            return 1;
        }
        if(n == 2) {
            return 2;
        }
        int a = 1;
        int b = 2;
        int temp = 0;
        for(int i = 3;i <=n; i++) {
            temp = a + b;
            a = b;
            b = temp;
        }
        return temp;
    }

這樣的時間複雜度只有o(n),顯然更加好。