<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          我是怎么向5歲侄女解釋動態(tài)規(guī)劃的?

          共 2481字,需瀏覽 5分鐘

           ·

          2021-01-26 14:10

          dp dp dp dp ....




          我膨脹了,相信看了這個標題的同學,肯定忍不住破口大罵,什么瓜皮哦,dynamic programming這么簡單嗎,在我的印象里,尼瑪動態(tài)規(guī)劃是最難的。


          >?背景


          侄女5歲現在開始學習加減法了,每次做數學都離不開她的小手指,超過5的就開始數另一個手的手指,真是汗顏啊


          有一天,我問她


          “1+1+1+1+1等于多少?”


          “搬起小拇指,就開始數了,5!”


          “那么再加一個1呢?”


          “當然是6了”?-脫口而出

          “這次你怎么算這么快的?”


          “剛剛不是5嗎,加1就是6了啊”


          “所以你不需要重新計算,因為你記得之前的答案是5!動態(tài)規(guī)劃就是說:記住之前的東西可以節(jié)省時間”



          進入正題


          玩歸玩,鬧歸鬧,別拿dp開玩笑!


          來瞅一眼科普中國科學百科的詞條解釋

          動態(tài)規(guī)劃(Dynamic Programming,DP)是運籌學的一個分支,是求解決策過程最優(yōu)化的過程。20世紀50年代初,美國數學家貝爾曼(R.Bellman)等人在研究多階段決策過程的優(yōu)化問題時,提出了著名的最優(yōu)化原理,從而創(chuàng)立了動態(tài)規(guī)劃。動態(tài)規(guī)劃的應用極其廣泛,包括工程技術、經濟、工業(yè)生產、軍事以及自動化控制等領域,并在背包問題、生產經營問題、資金管理問題、資源分配問題、最短路徑問題和復雜系統(tǒng)可靠性問題等中取得了顯著的效果。


          看不完的童鞋跳過,咱整點簡單點的


          其實剛剛這道題應該算是最簡單的動態(tài)規(guī)劃題目了。


          我們可以看出這道題有什么特點呢?


          我們知道之所以最后一步加1會計算的那么快,大部分要歸功于之前計算過答案是5,如果我們把問題歸納為一個子問題,我想要計算每一步的答案,就可以列出一個方程:f(x) = f(x -1) + 1, 大家別對f(x)發(fā)怵,就把它當做一個簡單的方法。


          其中,f(x)為第幾步的值,設定一個初始值,x > 0, 則第一步

          f(1) = 1;

          f(2)?= 2;

          ...

          f(6) =?6


          在程序的世界里,用什么東東來存一個可以記錄之前結果值的數據結構呢?


          顯而易見:數組唄。直接利用下標來存,巴適, 這就是動態(tài)規(guī)劃里的動態(tài),規(guī)劃就是限定一些邊界和初始化。



          來個訓練題


          別看了,這也是一道簡單的題

          leecode 322: 你有三種硬幣,2元、5元、7元,每種硬幣足夠多,買一本書需要27元,用最少的硬幣組合


          怎么感覺像是回到了小學應用題?


          --簡單分析一下:?最小硬幣組合 -> 盡量用大的硬幣


          這傻不拉幾的題誰出的,這么簡單


          7+7+7=21,21+2+2+2=27, 6枚硬幣


          臥槽


          7+5+5+5+5=27, 5枚硬幣


          我們可以很暴力的想一想,從大往小的算,可以加起來不超過27,比如

          7+7+7+7 > 27?(排除)

          7+7+7+5 或者?7?+7 +7+2+2+2?6枚

          ....

          窮舉太慢了,還會涉及到很多的重復計算,如果我想要27以內的任何值最小的硬幣組合呢,想想都頭大了吧。

          既然計算機可以通過內存保存之前的內容,又快,很明顯,我們開一個數組來保存之前的狀態(tài)。


          重點預警


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


          簡單的說,解動態(tài)規(guī)劃的時候需要開一個數組,數組的每個元素f[i]或者f[i][j]代表什么,類似數學題x, y, z代表什么


          解動態(tài)規(guī)劃需要兩個意識:

          • 最后一步

          • 子問題


          最后一步

          剛剛第一題不是說了嘛,最后一步的計算結果是5。對于這道題,雖然我們不知道最優(yōu)策略是什么,但是最優(yōu)策略肯定是K枚硬幣,a1, a2, ....ak面值加起來是27

          所以一定有一枚最后的硬幣:ak.

          除掉這么硬幣,前面硬幣的面值加起來是27-ak


          關鍵點1:

          • 我們不關心前面的k-1枚硬幣是怎么拼出27-ak的(可能有一種拼法,也可能有100種拼法),而且我們現在甚至還不知道ak和K, 但是我們確定前面的硬幣拼出了27-ak

          關鍵點2:

          • 因為是最優(yōu)策略, 所以拼出27-ak的硬幣數一定要最少,否則這就不是最優(yōu)策略了


          子問題

          • 所以我們就要求:最少用多少枚硬幣可以拼出27-ak

          • 原問題是最少用多少枚硬幣拼出27

          • 我們將原問題轉化了一個子問題,而且規(guī)模更小:27-ak

          • 為了簡化定義, 我們設狀態(tài)f(x)=最少用多少枚硬幣拼出x

          等等,我們還不知道最后的那枚硬幣ak是多少

          1. 很明顯,最后的那枚硬幣只能是2,5或者7

          2. 如果ak是2, f(27)應該是f(27-2)+1(1代表最后一枚硬幣2)

          3. 如果ak是5, f(27)應該是f(27-5)+1(1代表最后一枚硬幣5)

          4. 如果ak是7, f(27)應該是f(27-7)+1(1代表最后一枚硬幣7)

          所以使用最少的硬幣數=f(27) = min{f(27-2)+1, f(27-5)+1, f(27-7)+1}


          1.2. 動態(tài)規(guī)劃組成部分2:轉移方程

          設狀態(tài)f(x)=最少用多少枚硬幣拼出x

          對于任意的x : f(X) = min{f(X-2)+1, f(X-5)+1, f(X-7)+1}


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

          提出問題

          1. x-2, x-5, x-7 小于0怎么辦?

          2. 什么時候停下來?

          1.3.1

          如果不能拼出Y, 就定義f[Y] = 正無窮

          例如f[-1], f[-2] = 正無窮

          例如:拼不出f[1]=min{f(-1)+1, f(-3)+1, f(-6)+1}

          初始條件:f[0] = 0


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

          計算:f[1], f[2], ... f[27]

          當我們計算到f[x], f[x-2], f[x-5], f[x-7] 都已經得到結果了


          如圖:


          f[x] =?最少用多少枚硬幣拼出x

          f[x] =?∞?表示無法用硬幣拼出x


          參考代碼

          public??static??int?coinChange(int?[]?A,?int?M?)?{
          ?int[]?f?=?new?int[M+1];
          ?int?n?=?A.length;
          ?f[0]?=?0;
          ?int?i,j;
          ?for?(i?=?1;?i<=M;?i++)?{
          ????f[i]?=?Integer.MAX_VALUE;
          ????for?(j?=?0;?j // 邊界條件判斷
          ????????if?(i?>=?A[j]?&&?f[i?-?A[j]]?!=?Integer.MAX_VALUE)?{
          ????????????f[i]?=?Math.min(f[i?-?A[j]]?+?1,?f[i]);
          ??????????//??System.out.println(i?+?"="?+f[i]);
          ????????}
          ????}
          ?}
          ?if?(f[M]?==?Integer.MAX_VALUE)?{
          ????f[M]?=?-1?;
          ?}
          ?return??f[M];
          }


          ??

          核心代碼就4行,是不是很簡單~

          當然這道題也可以通過剪枝的方法實現,這里就不多贅述


          再來個訓練題



          還是再來一題吧,一道題也太隨意了~

          leecode 62 :不同路徑

          一個機器人位于一個 m x n 網格的左上角 (起始點在下圖中標記為 “Start” )。

          機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記為 “Finish” )。

          問總共有多少條不同的路徑?


          看了上面的解題步驟,按部就班的來

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

          最后一步

          無論機器人用何種方式到達右下角,總有最后挪動的一步:-向右或者向下

          如果所示,我們設右下角的坐標為(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)

          問題轉化為,機器人有多少種 方式從左上角走到(m-2,n-1)或者(m-1,m-2)

          如果走到是(m-2,n-1)如圖:

          我們可以直接干掉最后一列

          同理如果是走到(m-1,n-2)行就減少一行。

          狀態(tài)

          設f[i][j]為機器人有多少種方式從左上角走到(i,j)

          2.2. 動態(tài)規(guī)劃組成部分2:轉移方程

          對于任意一個格子:

          f[i][j] = f[i-1][j] + f[i][j-1]

          1 ? ? ? ? ?2 ? ? ? ? ? ?3

          1代表機器人有多少種方式走到[i][j]

          2代表機器人有多少種方式走到f[i-1][j]

          3代表機器人有多少種方式走到f[i][j-1]


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

          初始條件:f[0][0]=1,因為機器人只有一個方式到左上角

          邊界情況:i=0或j=0,則前一步只能有一個方向過來,也就是說第0行或者第0列,每走一步只有一種情況,則f[i][j] = 1,其他區(qū)域都滿足轉移方程。


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

          按行計算,為什么按行計算呢?

          對于這道題來說,按行計算在計算到f[1][1]時,f[0][1]和f[1][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]

          時間復雜度:O(mn)


          參考代碼

          public?int?uniquePaths(int?m,?int?n)?{
          ????int[][]?f?=?new?int[m][n];
          ????int?i,j;
          ????for?(i?=?0;?i<m;?i++)?{
          ????????for?(j?=?0;?jn
          ;?j++)?{
          ????????????if?(i?==?0?||?j?==?0)?{
          ????????????????f[i][j]?=?1;
          ????????????}?else?{
          ????????????????f[i][j]?=?f[i?-1][j]?+?f[i][j-1];
          ????????????}
          ????????}
          ????}
          ????return?f[m-1][n-1];
          }


          總結一下


          什么題可以選擇動態(tài)規(guī)劃來做?

          1.計數

          • 有多少種方式走到右下角

          • 有多少種方法選出k個數是的和是sum

          2.求最大值最小值

          • 從左上角走到右下角路徑的最大數字和

          • 最長上升子序列長度

          3.求存在性

          • 取石子游戲,先手是否必勝

          • 能不能選出k個數使得和是sum


          看到這里,基本上算是入門啦!






          瀏覽 18
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲色亭亭亭 | 乱伦导航| 五月天伊人大香蕉 | 自拍偷拍系列 | 黄片一级黄片 |