動態(tài)規(guī)劃1——入門

動態(tài)規(guī)劃(Dynamic Programming)題目特點

1. 計數(shù)
  • 有多少種方式走到右下角
  • 有多少種方法選出k個數(shù)使得和是Sum
2. 求最大最小值
  • 從左上角走到右下角路徑的最大數(shù)字和
  • 最長上升子序列長度
3. 求存在性
  • 取石子游戲乒裆,先手是否必勝
  • 能不能選出k個數(shù)使得和是Sum

例1:硬幣組合——最大最小值動態(tài)規(guī)劃

題目描述:
你有三種硬幣喝检,分別面值2元驹马,5元和7元队秩,每種硬幣都有足夠多
? 買一本書需要27元
? 如何用最少的硬幣組合正好付清或详,不需要對方找錢佩研。

直覺:
最少硬幣組合 → 盡量用面值大的硬幣
? 7+7+7 = 21
? 21 + 5 = 26
? 呃享完。。废膘。

改進:
盡量用大的硬幣竹海,最后如果可以用一種硬幣付清就行
? 7+7+7 = 21
? 21 + 2 + 2 + 2 = 27
? 6枚硬幣,應(yīng)該對了吧丐黄。斋配。。

然而灌闺,正確答案:7 + 5 + 5 + 5 + 5 = 27艰争,5枚硬幣。

動態(tài)規(guī)劃組成部分一:確定狀態(tài)

狀態(tài)在動態(tài)規(guī)劃中的作用屬于定海神針桂对。簡單的說甩卓,解動態(tài)規(guī)劃的時候需要開一個數(shù)組,數(shù)組的每個元素 f[i] 或者 f[i][j] 代表什么接校,類似于解數(shù)學題中猛频,X,Y蛛勉,Z代表什么鹿寻。確定狀態(tài)需要兩個意識:最后一步和子問題

  • 最后一步

雖然我們不知道最優(yōu)策略是什么诽凌,但是最優(yōu)策略肯定是 K 枚硬幣 a1, a2,…, aK 面值加起來是27毡熏,所以一定有一枚最后的硬幣 aK。除掉這枚硬幣侣诵,前面硬幣的面值加起來是27- aK痢法。

我們不關(guān)心前面的K-1枚硬幣是怎么拼出27- aK 的(可能有1種拼法,可能有 100種拼法)杜顺,而且我們現(xiàn)在甚至還不知道 aK 和 K财搁,但是我們確定前面的硬幣拼出了 27- aK 。因為是最優(yōu)策略躬络,所以拼出27- aK 的硬幣數(shù)一定要最少尖奔,否則這就不是最優(yōu)策略了。

  • 子問題

所以我們就要求:最少用多少枚硬幣可以拼出27- aK穷当。原問題是最少用多少枚硬幣拼出27提茁,我們將原問題轉(zhuǎn)化成了一個子問題,而且規(guī)模更心俨恕:27- aK茴扁。為了簡化定義,我們設(shè)狀態(tài) f(X) 等于最少用多少枚硬幣拼出X汪疮。

等等峭火,我們還不知道最后那枚硬幣aK是多少毁习。最后那枚硬幣 aK 只可能是2,5或者7卖丸。如果 aK 是2蜓洪,f(27)應(yīng)該是f(27-2) + 1 (加上最后這一枚硬幣2);如果 aK 是5坯苹,f(27)應(yīng)該是f(27-5) + 1 (加上最后這一枚硬幣5)隆檀;如果 aK 是7,f(27)應(yīng)該是f(27-7) + 1 (加上最后這一枚硬幣7)粹湃。除此以外恐仑,沒有其他的可能了 。

需要求最少的硬幣數(shù)为鳄,所以: f(27) = min{f(27-2)+1, f(27-5)+1, f(27-7)+1}

基于上述分析裳仆,可以使用遞歸的方式來解決:

def coin_change_re(x):
    if x == 0:
        return 0
    res = 1e15
    if x >= 2:
        res = min(ch_coin_re(x-2)+1, res)
    if x >= 5:
        res = min(ch_coin_re(x-5)+1, res)
    if x >= 7:
        res = min(ch_coin_re(x-7)+1, res)
    return res

但是有很多重復(fù)計算,效率低下孤钦。下圖計算了三次f(20):

解決方式:將計算結(jié)果保存下來歧斟,并改變計算順序。

動態(tài)規(guī)劃組成部分二:轉(zhuǎn)移方程

設(shè)狀態(tài)f[X]=最少用多少枚硬幣拼出X 偏形。

動態(tài)規(guī)劃組成部分三:初始條件和邊界情況

f[X] = min{f[X-2]+1, f[X-5]+1, f[X-7]+1}

兩個問題:
X-2, X-5 或者X-7小于0怎么辦静袖?什么時候停下來?
如果不能拼出Y,就定義f[Y]=正無窮。例如f[-1]=f[-2]=…=正無窮

所以:
初始條件:f[0] = 0
f[1] =min{f[-1]+1, f[-4]+1,f[-6]+1}=正無窮, 表示拼不出來1

動態(tài)規(guī)劃組成部分四:計算順序

? 拼出X所需要的最少硬幣數(shù):f[X] = min{f[X-2]+1, f[X-5]+1, f[X-7]+1}
? 初始條件:f[0] = 0
? 然后計算f[1], f[2], …, f[27]
? 當我們計算到f[X]時鸿摇,f[X-2], f[X-5], f[X-7]都已經(jīng)得到結(jié)果了。

f[0] = 0
f[1] = min{f[-1]+1, f[-4]+1,f[-6]+1} = ∞
f[2] = min{f[0]+1, f[-3]+1,f[-5]+1} = 1
f[3] = min{f[1]+1, f[-2]+1,f[-4]+1} = ∞
f[4] = min{f[2]+1, f[-1]+1,f[-3]+1} = 2
f[5] = min{f[3]+1, f[0]+1,f[-2]+1} = 1
f[6] = min{f[4]+1, f[1]+1,f[-1]+1} = 3
……
f[27] = 5

每一步嘗試三種硬幣捐康,一共27步。與遞歸算法相比庸蔼,沒有任何重復(fù)計算解总。算法時間復(fù)雜度(即需要進行的步數(shù)): 面額數(shù)x硬幣種類。這里是27x3姐仅。

代碼如下:

def coin_change(coins, amount):
    """
    換零錢動態(tài)規(guī)劃算法
    :param coins: 零錢種類整數(shù)列表
    :param amount: 需要換的面值
    :return: 最少換取的硬幣數(shù)
    """
    MAX_VALUE = 1e15
    states = [MAX_VALUE] * (amount+1)  # 狀態(tài)數(shù)組初始化花枫,包含狀態(tài)0
    states[0] = 0  # 初始值為0
    for i in range(1, amount+1):  # 依次求每個狀態(tài)
        for coin in coins:  # 遍歷所有硬幣種類,求最小值
            if i - coin < 0:
                continue
            states[i] = min(states[i], states[i-coin]+1)
    if states[amount] == MAX_VALUE:
        return -1
    return states[amount]
小結(jié)

求最值型動態(tài)規(guī)劃萍嬉,動態(tài)規(guī)劃組成部分:

  1. 確定狀態(tài)
    ? 最后一步(最優(yōu)策略中使用的最后一枚硬幣aK)
    ? 化成子問題(最少的硬幣拼出更小的面值27-aK)
  2. 轉(zhuǎn)移方程
    ? f[X] = min{f[X-2]+1, f[X-5]+1, f[X-7]+1}
  3. 初始條件和邊界情況
    ? f[0] = 0
    ? 如果不能拼出Y乌昔,f[Y]=正無窮
  4. 計算順序
    ? f[0], f[1], f[2], …

例2:不同的路徑數(shù)——計數(shù)型動態(tài)規(guī)劃

題目描述:
給定m行n列的網(wǎng)格隙疚,有一個機器人從左上角(0,0)出發(fā)壤追,每一步可以向下 或者向右走一步,問有多少種不同的方式走到右下角供屉。

組成部分一:確定狀態(tài)
  • 最后一步:無論機器人用何種方式到達右下角行冰,總有最后挪動的一步:向右或者向下溺蕉。右下角坐標設(shè)為(m-1, n-1) ,那么前一步機器人一定是在(m-2, n-1)或者(m-1, n-2) 悼做。

  • 子問題:如果機器人有X種方式從左上角走到(m-2,n-1)疯特,有Y種方式從左上 角走到(m-1,n-2),則機器人有X+Y種方式走到(m-1, n-1)肛走。問題轉(zhuǎn)化為漓雅,機器人有多少種方式從左上角走到(m-2, n-1)和(m-1, n-2)。原題要求有多少種方式從左上角走到(m-1, n-1)朽色。

  • 狀態(tài):設(shè) f[i][j] 為機器人有多少種方式從左上角走到(i, j)邻吞。

組成部分二:轉(zhuǎn)移方程
組成部分三:初始條件和邊界情況
  • 初始條件:f[0][0] = 1,因為機器人只有一種方式到左上角(什么都不做)
  • 邊界情況:i = 0 或 j = 0葫男,則前一步只能有一個方向過來抱冷。
組成部分四:計算順序
  • f[0][0] = 1
  • 計算第0行:f[0][0], f[0][1], …, f[0][n-1]
  • 計算第1行:f[1][0], f[1][1], …, f[1][n-1]
  • 計算第m-1行:f[m-1][0], f[m-1][1], …, f[m-1][n-1]
  • 答案是f[m-1][n-1]
  • 時間復(fù)雜度(計算步數(shù)):O(MN),空間復(fù)雜度(數(shù)組大猩液帧):O(MN)

代碼如下:

def unique_paths(m, n):
    """
    :param m: 網(wǎng)格行數(shù)
    :param n: 網(wǎng)格列數(shù)
    :return: 從左上角到右下角所有的路徑數(shù)
    """
    states = [[0] * n] * m  # 狀態(tài)數(shù)組
    states[0][0] = 1
    for i in range(m):
        for j in range(n):
            if i == 0 or j == 0:  # 邊界處都只有一條路可走
                states[i][j] = 1
            else:
                states[i][j] = states[i - 1][j] + states[i][j-1]
    return states[m-1][n-1]

例3:跳躍游戲——存在型動態(tài)規(guī)劃

題目描述:
有n塊石頭分別在x軸的0, 1, …, n-1位置旺遮,一只青蛙在石頭0,想跳到石頭n-1盈咳。如果青蛙在第 i 塊石頭上耿眉,它最多可以向右跳距離ai 。問青蛙能否跳到石頭n-1鱼响?
例子:
輸入:a=[2, 3, 1, 1, 4] 輸出:True
輸入:a=[3, 2, 1, 0, 4] 輸出:False

組成部分一:確定狀態(tài)
  • 最后一步:如果青蛙能跳到最后一塊石頭n-1跷敬,我們考慮它跳的最后一步,這一步是從石頭i跳過來热押,i<n-1西傀。這需要兩個條件同時滿足:青蛙可以跳到石頭i;最后一步不超過跳躍的最大距離:n-1-i<=ai 桶癣。
  • 子問題:那么拥褂,我們需要知道青蛙能不能跳到石頭i (i<n-1),而我們原來要求青蛙能不能跳到石頭n-1牙寞。
  • 狀態(tài):設(shè) f[j] 表示青蛙能不能跳到石頭 j 饺鹃。
組成部分二:轉(zhuǎn)移方程
組成部分三:初始條件和邊界情況

初始條件:f[0] = True,因為青蛙一開始就在石頭0间雀。

組成部分四:計算順序

? 設(shè)f[j]表示青蛙能不能跳到石頭j
? f[j] = OR_{0<=i<j}((f[i]) and( i + a[i] >= j))
? 初始化 f[0]=True
? 計算 f[1], f[2], …, f[n-1]
? 答案是 f[n-1]
? 時間復(fù)雜度:O(N2)悔详,空間復(fù)雜度(數(shù)組大小):O(N)

代碼如下:

def jump_game(n, lst):
    states = [False] * n
    states[0] = True
    for i in range(1, n):
        for j in range(i):
            if states[j] and lst[j] + j >= i:
                states[i] = True
                break
    return states[n-1]

以上代碼時間復(fù)雜度為 O(N2)惹挟,一般會運行超時茄螃,但是也是需要掌握的。優(yōu)化后的代碼(時間復(fù)雜度O(N)):

 def jump_game(n, lst):
        max_reach = 0
        for i, x in enumerate(lst): 
            if max_reach < i: return False # 如果之前的最遠距離下標连锯,小于當前的下標归苍,就gg
            if max_reach >= n - 1: return True # 或者大于最遠直接返回True
            max_reach = max(max_reach, i + x)  # 每一步更新可以跳到的最遠距離下標

總結(jié)

四個組成部分:

  • 確定狀態(tài)
    ? 研究最優(yōu)策略的最后一步
    ? 化為子問題
  • 轉(zhuǎn)移方程
    ? 根據(jù)子問題定義直接得到
  • 初始條件和邊界情況
    ? 細心用狱,考慮周全
  • 計算順序
    ? 利用之前的計算結(jié)果

常見動態(tài)規(guī)劃類型:

  • 坐標型動態(tài)規(guī)劃 (20%)
  • 序列型動態(tài)規(guī)劃 (20%)
  • 劃分型動態(tài)規(guī)劃 (20%)
  • 區(qū)間型動態(tài)規(guī)劃 (15%)
  • 背包型動態(tài)規(guī)劃 (10%)
  • 拓撲型動態(tài)規(guī)劃 (5%)
  • 博弈型動態(tài)規(guī)劃 (5%)
  • 綜合性動態(tài)規(guī)劃 (5%)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拼弃,隨后出現(xiàn)的幾起案子夏伊,更是在濱河造成了極大的恐慌,老刑警劉巖吻氧,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件溺忧,死亡現(xiàn)場離奇詭異,居然都是意外死亡盯孙,警方通過查閱死者的電腦和手機砸狞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镀梭,“玉大人刀森,你說我怎么就攤上這事”ㄕ耍” “怎么了研底?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長透罢。 經(jīng)常有香客問我榜晦,道長,這世上最難降的妖魔是什么羽圃? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任乾胶,我火速辦了婚禮,結(jié)果婚禮上朽寞,老公的妹妹穿的比我還像新娘识窿。我一直安慰自己,他們只是感情好脑融,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布喻频。 她就那樣靜靜地躺著,像睡著了一般肘迎。 火紅的嫁衣襯著肌膚如雪甥温。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天妓布,我揣著相機與錄音姻蚓,去河邊找鬼。 笑死匣沼,一個胖子當著我的面吹牛狰挡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼圆兵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了枢贿?” 一聲冷哼從身側(cè)響起殉农,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎局荚,沒想到半個月后超凳,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡耀态,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年轮傍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片首装。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡创夜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仙逻,到底是詐尸還是另有隱情驰吓,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布系奉,位于F島的核電站檬贰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏缺亮。R本人自食惡果不足惜翁涤,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望萌踱。 院中可真熱鬧葵礼,春花似錦、人聲如沸并鸵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽能真。三九已至赁严,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間粉铐,已是汗流浹背疼约。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝙泼,地道東北人程剥。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親织鲸。 傳聞我的和親對象是個殘疾皇子舔腾,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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

  • 父親在我心中的形象一直是高大、嚴厲的搂擦。 小時候的我對他有種“恐懼感”稳诚。因為他對我特別嚴格,我在數(shù)學題的...
    毛毛的小小世界閱讀 481評論 0 3
  • 還有弼馬溫
    kamia閱讀 193評論 0 0
  • 靜淑: 今天我早早起來瀑踢,裹著毛毯坐在甲板上等日出扳还。海風習習,大海上東升的太陽顯得比家鄉(xiāng)的大了許多橱夭。我便想起來...
    林靜風閑閱讀 309評論 0 0
  • 大小目標造起來氨距。從2018.1.22開始和家里的小孩暫別一個月,這段時間關(guān)于生活應(yīng)該有規(guī)律棘劣。每天5點起床俏让,把工作做...
    云之谷溪閱讀 161評論 0 1
  • 這城市,昏昏然的茬暇,傾斜了舆驶。 并不是那種寫意的傾斜,而是那種寫實的傾斜而钞。 其實我早就料到沙廉,這樣一座城,它...
    長安舊煙閱讀 288評論 0 0