理論+實踐,帶你掌握動態規劃法

華為雲開發者社群發表於2022-02-28
摘要:本文介紹了動態規劃法的基本概念,通過詳細解析動態規劃法的特徵,給出判斷問題是否使用動態規劃法結題的思路。

本文分享自華為雲社群《五大基礎演算法--動態規劃法》,作者: 大金(內蒙的)。

一、基本概念

動態規劃法,和分治法極其相似。區別就是,在求解子問題時,會儲存該子問題的解,後面的子問題求解時,可以直接拿來計算。

專業的說法是:

對於一個規模為n的問題,將其分解為k個規模較小的子問題(階段),按順序求解子問題,前一子問題的解,為後一子問題的求解提供了有用的資訊。在求解任一子問題時,通過決策求得區域性最優解,依次解決各子問題。最後可以通過簡單的判斷,得到原問題的解。

說法有些晦澀難懂,我給大家解釋下:

階段:求解第n個子問題稱為第n個階段。動態規劃是按照順序去求解子問題的,這裡子問題的求解順序很重要。
狀態:在求解第n個階段時,已求解n-1個階段的解,稱為狀態。
決策:在求解第n個階段時,根據狀態和計算規則,可以得到第n個階段時解。

二、基本特徵

動態規劃法所能解決的問題一般具有以下幾個特徵:

1) 大問題可分解性

該問題可以分解為若干個規模較小的問題,即該問題具有最優子結構性質。

2) 子問題易解決性

該問題的規模縮小到一定的程度就可以容易地解決

3) 解可合併性

利用該問題分解出的子問題的解可以合併為該問題的解;

4) 子問題重疊性

當計算出某個子問題的解時,後續多個問題都需要計算該子問題的解,所以在計算某個子問題的解,將其儲存,就節省了分治法重複計算的時間。

三、一些誤解

1.狀態轉移方程

很多部落格都說什麼狀態轉移方程,感覺說的很高大上,一般解題上來就是狀態轉移方程是xxxx,程式碼是xxxx,翻譯下是什麼意思呢?
在求解第n個子問題的時候,通過已求解n-1個階段的解和計算規則,可以得到第n個階段時解。
即是最新的狀態=目前的狀態+決策。

2.初始化

很多題目解題的時候都說初始化,這並不是動態規劃法的步驟。應該正確的去理解這些操作。動態規劃在劃分子問題求解順序時,基本是先求解易求解最小的子問題,在由這些已經求解的階段+計算規則,就能直接求得第n階段的解。所以初始化的含義是,求得初始階段的解。

3.邊界條件

一般題目會說邊界是啥,可以理解為怎麼判斷所有的子問題已經求解結束了。正常人也不會寫while(true)吧,你總得讓程式結束,判斷你已經解決好這個問題了。

四、動態規劃法的基本步驟

step1 分解:

將一個問題分解為多個子問題,需要注意子問題解決的順序,應該先求解易求解的子問題,且後續的階段可以通過前面的階段+決策得到。

step2 狀態轉移:

通過得到的規律,寫出狀態轉移方程。
第n階段=當前狀態+決策(前n個階段解和計算規則)

step3 寫程式碼:

將最先算的階段計算出來,中間階段通過狀態轉移方程計算狀態,直到所有階段計算結束。

step4 得到解:

所有階段計算結束,可以通過簡單的統計,例如Max,Min等遍歷階段的值,得到最後的解。

五、經典問題

好記性不如爛筆頭,有一些適用動態規劃法的問題,可以幫我們不斷強化的解題思想。在解決問題時,希望大家可以注意判斷題目的解決思路,看是否符合動態規劃法的四個特徵,這樣不斷強化,才能將演算法掌握。

最長迴文子串

下面附上我的題解:

https://leetcode-cn.com/problems/longest-palindromic-substring/solution/dong-tai-gui-hua-fa-qiu-jie-kan-bu-dong-octkt/

//動態規劃法兩個基本要素:最優子結構性質和子問題重疊性質。
//很多答案寫了初始化和邊界條件,個人認為你要分清楚他的目的是什麼。
//很多初始化和邊界條件,是因為狀態轉移方程,是需要初始化的子問題的解,從而避免重複計算,說白了還是子問題重疊和最優子結構問題。
//我們應該注重某一個問題的重疊子問題分解和狀態最優的決策分析。
//解題思路:
//計算某個字串時, 如果它首尾字元相等,則它是不是迴文,取決於去掉頭尾之後的字串是否為迴文串。
//                  如果它首尾字元不相等,則它一定不是迴文
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        // 特判
        if (len < 2){
            return s;
        }

        int maxLen = 1;
        int begin  = 0;

        // 1. 狀態定義
        // dp[i][j] 表示s[i...j] 是否是迴文串,現在表示全部為0,不是迴文串
        boolean[][] dp = new boolean[len][len];
        char[] chars = s.toCharArray();
        // 2. 子問題計算順序:先計算短字串,在計算長字串,同時根據已求得的短字串或者計算規則,可以得到長字串的解。

            // 注意:s表示計算的元素順序。
        //     0   1   2  3  4
        // 0   xx s1  s2 s4 s7
        // 1      xx  s3 s5 s8
        // 2          xx s6 s9
        // 3             xx s10
        // 4                xx
        // 為什麼這麼寫呢,因為你要保證保證計算某個元素時,通過狀態轉移方程能得到左上角元素的dp[][]。
        // 填表規則:先一列一列的填寫,再一行一行的填,保證計算某個元素時,它左上方的單元格已經被計算出了結果
        // 填表規則:當然你也可以由左往右一行一行寫,這樣也能保證計算某個元素時,它左上方的單元格已經被計算出了結果
        for (int j = 1;j < len;j++){
            for (int i = 0; i < j; i++) {
                // 頭尾字元不相等,不是迴文串
                if (chars[i] != chars[j]){
                    dp[i][j] = false;
                }else {
                    // 相等的情況下
                    // 因為考慮頭尾去掉以後沒有字元剩餘,或者剩下一個字元的時候,肯定是迴文串
                    if (j - i -1 <= 1){
                        dp[i][j] = true;
                    }else {
                        // 頭尾相等,中間有大於1個元素,這個時候,我們無法直接判斷他是不是迴文,但是我們可以通過狀態轉移方程去判斷

                        // 其實這個就是在計算s8這個元素時,我們無法判斷dp[1][4]在1和4位元素相等時候,整個字串是否是迴文。
                        // 所以要通過s4去判斷,s4是迴文,s8就是。s4不是,那s8就不是。
                        dp[i][j] = dp[i + 1][j - 1];
                    }
                }

                // 只要dp[i][j] == true 成立,表示s[i...j] 是否是迴文串
                // 此時更新記錄迴文長度和起始位置
                if (dp[i][j] && (j - i + 1 > maxLen)){
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }

        // 3. 初始化  
        // 很多答案寫了這個,這一步,我們細想,其實完全沒有必要。
        // 因為主對角線,值是可以直接判斷出來的。
        // 而且在求解過程中,我們的狀態轉移方程不會用到這個值。因為只有主對角線會用到這幾個值。
        // 而且單個元素的子問題解,我們並不需要。
        // 所以,即使我這步初始化放到計算之後,甚至是直接去掉,也完全不影響結果。大家可以自己試一下
        // for (int i = 0; i < len; i++) {
        //     dp[i][i] = true;
        // }
        // 4. 返回值
        return s.substring(begin,begin + maxLen);
    }
}

 

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章