動態(tài)規(guī)劃問題

動態(tài)規(guī)劃(英語:Dynamic programming,DP)是一種在數(shù)學(xué)锹杈、計算機(jī)科學(xué)經(jīng)濟(jì)學(xué)中使用的撵孤,通過把原問題分解為相對簡單的子問題的方式求解復(fù)雜問題的方法。 動態(tài)規(guī)劃常常適用于有重疊子問題最優(yōu)子結(jié)構(gòu)性質(zhì)的問題竭望,動態(tài)規(guī)劃方法所耗時間往往遠(yuǎn)少于樸素解法邪码。

動態(tài)規(guī)劃背后的基本思想非常簡單撒轮。大致上只锭,若要解一個給定問題,我們需要解其不同部分(即子問題)翼闹,再合并子問題的解以得出原問題的解枫振。 通常許多子問題非常相似喻圃,為此動態(tài)規(guī)劃法試圖僅僅解決每個子問題一次,從而減少計算量: 一旦某個給定子問題的解已經(jīng)算出粪滤,則將其記憶化存儲,以便下次需要同一個子問題解之時直接查表杖小。 這種做法在重復(fù)子問題的數(shù)目關(guān)于輸入的規(guī)模呈指數(shù)增長時特別有用愚墓。

動態(tài)規(guī)劃問題滿足三大重要性質(zhì)

最優(yōu)子結(jié)構(gòu)性質(zhì):如果問題的最優(yōu)解所包含的子問題的解也是最優(yōu)的昂勉,我們就稱該問題具有最優(yōu)子結(jié)構(gòu)性質(zhì)(即滿足最優(yōu)化原理)。最優(yōu)子結(jié)構(gòu)性質(zhì)為動態(tài)規(guī)劃算法解決問題提供了重要線索岗照。

子問題重疊性質(zhì):子問題重疊性質(zhì)是指在用遞歸算法自頂向下對問題進(jìn)行求解時,每次產(chǎn)生的子問題并不總是新問題厚者,有些子問題會被重復(fù)計算多次。動態(tài)規(guī)劃算法正是利用了這種子問題的重疊性質(zhì)库菲,對每一個子問題只計算一次志膀,然后將其計算結(jié)果保存在一個表格中,當(dāng)再次需要計算已經(jīng)計算過的子問題時溉浙,只是在表格中簡單地查看一下結(jié)果,從而獲得較高的效率放航。

無后效性:將各階段按照一定的次序排列好之后,對于某個給定的階段狀態(tài)广鳍,它以前各階段的狀態(tài)無法直接影響它未來的決策,而只能通過當(dāng)前的這個狀態(tài)赊时。換句話說,每個狀態(tài)都是過去歷史的一個完整總結(jié)祖秒。這就是無后向性,又稱為無后效性房维。

簡單dp問題分類

1.簡單動態(tài)規(guī)劃算法咙俩,即狀態(tài)方程是用一個維度的變量的描述的,常見的問題如:斐波那契數(shù)列膜蛔,爬臺階問題等

爬臺階問題問題描述: 有一座高度是n級臺階的樓梯皂股,從下往上走命黔,每跨一步只能向上1級或者2級臺階。要求用程序來求出一共有多少種走法卵史。
狀態(tài)描述: 我們使用變量n表示臺階的級數(shù),F(xiàn)(n)表示n級臺階一共有多少種走法
狀態(tài)轉(zhuǎn)移方程與問題分解: 根據(jù)每次能跨越的臺階數(shù)目:1級臺階或者2級臺階啄踊,因為走到N級臺階之前刁标,人一定是處于N-1級臺階或者N-2級臺階膀懈。F(n)的走法,一定是n-1級別的臺階的所有的走法和n-2級別臺階的所有走法之和硼控。
F(n) = F(n-1) + F(n-2);

    int climbStairs(int n) {
        vector<int>dp(n+1,0);
        dp[0]=1;
        dp[1]=1;
        for(int i=2;i<=n;i++)
            dp[i]=dp[i-1]+dp[i-2];
        return dp.back();
    }

2.最小路徑和問題牢撼,常見題型:矩陣最小路徑和熏版、三角形最小路徑和

leetcode64
給定一個包含非負(fù)整數(shù)的 m x n 網(wǎng)格撼短,請找出一條從左上角到右下角的路徑,使得路徑上的數(shù)字總和為最小曲横。
說明:每次只能向下或者向右移動一步胜榔。
示例:
輸入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
輸出: 7
解釋: 因為路徑 1→3→1→1→1 的總和最小夭织。

    int minPathSum(vector<vector<int>>& grid) {
        int m=grid.size();
        int n=grid[0].size();
        int dp[m][n];
        dp[0][0]=grid[0][0];
        for(int i=1;i<m;i++)
        {
            dp[i][0]=dp[i-1][0]+grid[i][0];
        }
        for(int j=1;j<n;j++)
        {
            dp[0][j]=dp[0][j-1]+grid[0][j];
        }
        for(int i=1;i<m;i++)
        {
            for(int j=1;j<n;j++)
            {
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j];
            }
        }
        return dp[m-1][n-1];
    }

leetcode120
給定一個三角形,找出自頂向下的最小路徑和讲竿。每一步只能移動到下一行中相鄰的結(jié)點上题禀。
例如迈嘹,給定三角形:



自頂向下的最小路徑和為 11(即秀仲,2 + 3 + 5 + 1 = 11)

    int minimumTotal(vector<vector<int>>& triangle) {
        if(triangle.empty()||triangle[0].empty()) return 0;
        for(int i=1;i<triangle.size();i++)
        {
            for(int j=0;j<triangle[i].size();j++)
            {
                if(j==0)
                    triangle[i][j]+=triangle[i-1][j];
                else if(j==triangle[i].size()-1)
                    triangle[i][j]+=triangle[i-1][j-1];
                else
                    triangle[i][j]+=min(triangle[i-1][j],triangle[i-1][j-1]);
            }
        }
        return *min_element(triangle.back().begin(),triangle.back().end());   
    }

3.背包問題

一個背包有一定的承重cap神僵,有N件物品保礼,每件都有自己的價值炮障,記錄在數(shù)組v中鹦筹,也都有自己的重量,記錄在數(shù)組w中铐拐,每件物品只能選擇要裝入背包還是不裝入背包遍蟋,要求在不超過背包承重的前提下,選出物品的總價值最大它呀。
給定物品的重量w價值v及物品數(shù)n和承重cap下隧。請返回最大總價值谓媒。
測試樣例:
[1,2,3],[1,2,3],3,6
返回:6

int maxvalue(vector<int> &weight,vector<int> &value,int n,int m)
{
  vector<int> f(weight.size()+1,0);
  for (int i = 1; i <= n; i++) {
       for (int j = m; j >= 1; j--) {
           if (weight[i] <= j) {
               f[j] = f[j] > f[j - weight[i]] + value[i] ? f[j] : f[j - weight[i]] + value[i];
           }
       }
  }
}

leetcode 322
給定不同面額的硬幣 coins 和一個總金額 amount土辩。編寫一個函數(shù)來計算可以湊成總金額所需的最少的硬幣個數(shù)拷淘。如果沒有任何一種硬幣組合能組成總金額,返回 -1启涯。
示例 1:
輸入: coins = [1, 2, 5], amount = 11
輸出: 3
解釋: 11 = 5 + 5 + 1
示例 2:

輸入: coins = [2], amount = 3
輸出: -1

    int coinChange(vector<int>& coins, int amount) {
        int n=coins.size();
        if(n<=0 || amount<0) return -1;
        vector<int>dp(amount+1,amount+1);
        dp[0]=0;
        for(int i=1;i<=amount;i++)
        {
            for(int j=0;j<coins.size();j++ )
            {
                if(coins[j]<=i)
                    dp[i]=min(dp[i],dp[i-coins[j]]+1);
            }
        }
        return (dp[amount]>amount) ?-1:dp[amount];
    }

4.最長上升子序列問題

leetcode 300
給定一個無序的整數(shù)數(shù)組,找到其中最長上升子序列的長度。
示例:
輸入: [10,9,2,5,3,7,101,18]
輸出: 4
解釋: 最長的上升子序列是 [2,3,7,101]引几,它的長度是 4伟桅。

    int lengthOfLIS(vector<int>& nums) {
        if(nums.empty()) return 0;
        vector<int> dp(nums.size(),1);
        int res=0;
        for(int i=0;i<nums.size();++i)
        {
            for(int j=0;j<i;j++)
            {
            if(nums[i]>nums[j])
                dp[i]=max(dp[i],dp[j]+1);
            }
            res=max(res,dp[i]);
        }
        return res;
    }

5.回文子串問題

leetcode5
給定一個字符串 s玖雁,找到 s 中最長的回文子串赫冬。你可以假設(shè) s 的最大長度為 1000溃列。
示例 1:
輸入: "babad"
輸出: "bab"
注意: "aba" 也是一個有效答案听隐。
示例 2:
輸入: "cbbd"
輸出: "bb"

    string longestPalindrome(string s) {
        if(s.empty()) return "";
        const int n=s.size();
        int dp[n][n];
        int left=0,right=0,len=0;;
        for(int i=0;i<s.size();i++)
        {
            dp[i][i]=1;
            for(int j=0;j<i;i++)
            {
                dp[j][i]= (s[i]==s[j]) &&(i-j<2 || dp[j+1][i-1]);
                if(dp[j][i] && len<i-j+1)
                {
                    len=i-j+1;
                    left=j;
                    right=i;  
                }
            }
            return s.substr(left,right-left+1);
        }
    }

6.最長上升子序列問題(LCS)

leetcode1143
給定兩個字符串 text1 和 text2咨跌,返回這兩個字符串的最長公共子序列硼婿。
一個字符串的 子序列 是指這樣一個新的字符串:它是由原字符串在不改變字符的相對順序的情況下刪除某些字符(也可以不刪除任何字符)后組成的新字符串加酵。
例如猪腕,"ace" 是 "abcde" 的子序列陋葡,但 "aec" 不是 "abcde" 的子序列。兩個字符串的「公共子序列」是這兩個字符串所共同擁有的子序列捌归。
若這兩個字符串沒有公共子序列,則返回 0剃浇。
示例 1:
輸入:text1 = "abcde", text2 = "ace"
輸出:3
解釋:最長公共子序列是 "ace",它的長度為 3

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int m=text1.size()+1;
        int n=text2.size()+1;
        int dp[m][n]={};
        for(int i=1;i<m;++i)
        {
            for(int j=1;j<n;++j)
            {
               if(text1[i-1]==text2[j-1])
               {
                   dp[i][j]=dp[i-1][j-1]+1;
               }
               else
                 dp[i][j]=max(dp[i-1][j],dp[i][j-1]); 
            }
        }
        return dp[m-1][n-1];
    }
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市淘讥,隨后出現(xiàn)的幾起案子蒲列,更是在濱河造成了極大的恐慌嫉嘀,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剪侮,死亡現(xiàn)場離奇詭異拭宁,居然都是意外死亡洛退,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門杰标,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兵怯,“玉大人,你說我怎么就攤上這事腔剂∶角” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵掸犬,是天一觀的道長袜漩。 經(jīng)常有香客問我,道長宙攻,這世上最難降的妖魔是什么柔滔? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任超全,我火速辦了婚禮见咒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己庆揩,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布娄昆。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天辣往,我揣著相機(jī)與錄音坊萝,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛狮崩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哑舒,長吁一口氣:“原來是場噩夢啊……” “哼膘滨!你這毒婦竟也來了铲咨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鉴逞,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡若未,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年隙疚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辙芍。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡庶灿,死狀恐怖瘦癌,靈堂內(nèi)的尸體忽然破棺而出讯私,到底是詐尸還是另有隱情楞黄,我是刑警寧澤碎税,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響砸狞,放射性物質(zhì)發(fā)生泄漏飘哨。R本人自食惡果不足惜胚想,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望芽隆。 院中可真熱鬧浊服,春花似錦、人聲如沸胚吁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腕扶。三九已至孽拷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間半抱,已是汗流浹背脓恕。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留窿侈,地道東北人炼幔。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像史简,于是被迫代替她去往敵國和親乃秀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容