采用動(dòng)態(tài)規(guī)劃的思想解決三種經(jīng)典問(wèn)題之Java實(shí)現(xiàn)

當(dāng)遇到復(fù)雜問(wèn)題時(shí)我們經(jīng)常會(huì)通過(guò)遞歸的方式將大事化小浓瞪,小事化了。但是有時(shí)候?qū)?fù)雜問(wèn)題簡(jiǎn)單地分解成幾個(gè)子問(wèn)題,問(wèn)題求解耗時(shí)會(huì)按問(wèn)題規(guī)模呈冪級(jí)數(shù)增加褐健。這時(shí)候?yàn)榱斯?jié)約重復(fù)求相同子問(wèn)題的時(shí)間,引入一個(gè)數(shù)組(或臨時(shí)變量)澜汤,不管它們是否對(duì)最終解有用蚜迅,把所有子問(wèn)題的解存于該數(shù)組(或臨時(shí)變量)中,這就是動(dòng)態(tài)規(guī)劃的基本思想俊抵。

1.Fibonacci數(shù)列

該數(shù)列的遞歸形式如下谁不,即從每二項(xiàng)開(kāi)始每一項(xiàng)等于前兩項(xiàng)之和:

根據(jù)定義可以用遞歸方式寫(xiě)出如下代碼:

public int fib(int n) { // 計(jì)算Fibonacci數(shù)列的第n項(xiàng)(二分遞歸版):O(2^n)
        return (2 > n) ? n // 若到達(dá)遞歸基,直接取值
                : fib(n - 1) + fib(n - 2); // 否則徽诲,遞歸計(jì)算前兩項(xiàng)刹帕,其和即為正解
}

遞規(guī)版本的時(shí)間復(fù)雜度將呈指數(shù)級(jí)別,明顯不能很好的解決問(wèn)題谎替,現(xiàn)通過(guò)犧牲空間復(fù)雜度偷溺,利用動(dòng)態(tài)規(guī)劃的思想將時(shí)間復(fù)雜度控制降低到O(n).

public static int fib1(int n) { // 計(jì)算Fibonacci數(shù)列的第n項(xiàng)(動(dòng)態(tài)規(guī)劃版):O(n)
        if(n < 2) return n;
        int a = 0;//第一個(gè)值
        int b = 1;//第二個(gè)值
        int tmp = 0;//輔助變量
        for(int i = 2;i <= n;i++){
            tmp = a + b;
            a = b;
            b = tmp;
        }
        return tmp;     
    }

2.爬樓梯

問(wèn)題描述:有一座高度為n級(jí)的樓梯,從下往上走钱贯,每跨一步只能向上1級(jí)或者2級(jí)臺(tái)階挫掏,求出一共有多少種走法。

分析:因?yàn)槊?步只能走1級(jí)或者2級(jí)秩命,所以走到第n級(jí)的前一步有且只有兩種情況砍濒,第一種情況是從第n - 1級(jí)走1級(jí)淋肾,第二種情況是從第n - 2級(jí)走2級(jí)。由此我們就可以得到此問(wèn)題的遞歸公式:

F(1) = 1;
F(2) = 2;
F(n) = F(n - 1) + F(n - 2);

看到這里爸邢,相信聰明的你很快就反映過(guò)來(lái)了這不就是上面的Fibonacci數(shù)列問(wèn)題嘛(注意:起始值不同)樊卓,下面直接給出動(dòng)態(tài)規(guī)劃思想的代碼實(shí)現(xiàn):

public static int getNum(int n) { 
        if(n <= 2) return n;
        int a = 1;//只有1級(jí)臺(tái)階的情況
        int b = 2;//有2級(jí)臺(tái)階的情況
        int tmp = 0;//輔助變量
        for(int i = 3;i <= n;i++){
            tmp = a + b;
            a = b;
            b = tmp;
        }
        return tmp;     
    }

3.最長(zhǎng)公共子序列

有了上面兩個(gè)動(dòng)態(tài)規(guī)劃實(shí)現(xiàn)的熱身,下面看一個(gè)復(fù)雜點(diǎn)的問(wèn)題杠河,首先看一下兩個(gè)定義:
子序列(Sequence):由序列中若干字符碌尔,按原相對(duì)次序構(gòu)成。

最長(zhǎng)公共子序列(Longest Common Subsequence):兩個(gè)序列公共子序列中的最長(zhǎng)者券敌,下面簡(jiǎn)稱(chēng)LCS,可能出現(xiàn)下面兩種比較復(fù)雜的情況:
1).可能有多個(gè)

2).可能有歧義

現(xiàn)用遞歸的思想分析如下:
對(duì)于序列A[0,n]和B[0,m]唾戚,LCS(A,B)無(wú)非三種情況:

  1. 若n = - 1或 m = - 1,則取作空序列("")
  2. 若A[n] = ‘X' = B[m],則取作LCS(A[0,n), B[0,m)) + 'X',示意圖如下:
  1. 若A[n] != B[m],則在LCS(A[0,n], B[0,m))與LCS(A[0,n), B[0,m])中取更長(zhǎng)者
    示意圖如下:


如下圖所示待诅,展示了“EDUCATIONAL”和“ADVANTAGE”通過(guò)遞歸思想求最長(zhǎng)公共子序列的過(guò)程叹坦。


代碼實(shí)現(xiàn)如下:

    /**
     * 遞歸方式實(shí)現(xiàn)求兩個(gè)字符串最長(zhǎng)公共字序列的長(zhǎng)度
     * @param str1 第一個(gè)字符串
     * @param m 第一個(gè)字符串需要比較的長(zhǎng)度
     * @param str2 第二個(gè)字符串
     * @param n 第一個(gè)字符串需要比較的長(zhǎng)度
     * @return
     */
    public static int lcs(String str1,int m,String str2,int n){
        if(m == 0 || n == 0) return 0;//如果其中有一個(gè)元素為空則返回0
        if(str1.charAt(m - 1) == str2.charAt(n - 1)) 
            return lcs(str1,m - 1,str2,n - 1) + 1;//如果需要比較的兩個(gè)字符串最后一個(gè)字符相同則將問(wèn)題縮小
        //剩下的情況則兩個(gè)字符串的最后一個(gè)字符不相等,取兩種情況中的最大值
        return getMax(lcs(str1,m - 1,str2,n),lcs(str1,m,str2,n - 1));
    }

可以看出如果最后一個(gè)字符不相等卑雁,反而會(huì)將問(wèn)題的規(guī)模擴(kuò)大募书,當(dāng)m與n大小接近時(shí),時(shí)間復(fù)雜度也呈指數(shù)級(jí)別测蹲。下面將考慮通過(guò)動(dòng)態(tài)規(guī)劃的思想來(lái)實(shí)現(xiàn):
構(gòu)造一個(gè)二維輔助數(shù)組莹捡,從上往下依次計(jì)算,如下圖所示:

代碼實(shí)現(xiàn)如下:

    /**
     * 非遞歸方式實(shí)現(xiàn)求兩個(gè)字符串最長(zhǎng)公共字序列的長(zhǎng)度
     * @param str1 第一個(gè)字符串
     * @param m 第一個(gè)字符串需要比較的長(zhǎng)度
     * @param str2 第二個(gè)字符串
     * @param n 第一個(gè)字符串需要比較的長(zhǎng)度
     * @return
     */
    public static int lcs1(String str1,int m,String str2,int n){
        if(m == 0 || n == 0) return 0;
        //構(gòu)建一個(gè)m + 1行 n + 1列的輔助二維數(shù)組,里面的元素初始值都為0
        int[][] arr = new int[m + 1][n + 1];
        //依次求二維數(shù)組中的值
        for(int i = 1;i <= m;i ++){
            for(int j = 1;j <= n; j ++){
                //當(dāng)最后一個(gè)字符相等時(shí)等于左上角的元素加1
                if(str1.charAt(i - 1) == str2.charAt(j - 1)) arr[i][j] = arr[i - 1][j - 1] + 1;
                //不相等時(shí)取緊鄰左邊元素和上邊元素者的大者
                else arr[i][j] = getMax(arr[i - 1][j],arr[i][j - 1]);
            }
        }
        return arr[m][n];//二維數(shù)組右下角的元素即我們需要的值
    }

總結(jié):動(dòng)態(tài)規(guī)劃的思想通過(guò)犧牲空間復(fù)雜度的方式來(lái)大大減小時(shí)間復(fù)雜度扣甲,將自頂而下的計(jì)算方式改為自底而上篮赢,把已計(jì)算過(guò)的實(shí)例結(jié)果制表備查,從而取得非常顯著的效果琉挖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末启泣,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子示辈,更是在濱河造成了極大的恐慌种远,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顽耳,死亡現(xiàn)場(chǎng)離奇詭異坠敷,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)射富,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)膝迎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人胰耗,你說(shuō)我怎么就攤上這事限次。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵卖漫,是天一觀的道長(zhǎng)费尽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)羊始,這世上最難降的妖魔是什么旱幼? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮突委,結(jié)果婚禮上柏卤,老公的妹妹穿的比我還像新娘。我一直安慰自己匀油,他們只是感情好缘缚,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著敌蚜,像睡著了一般桥滨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弛车,一...
    開(kāi)封第一講書(shū)人閱讀 52,584評(píng)論 1 312
  • 那天齐媒,我揣著相機(jī)與錄音,去河邊找鬼帅韧。 笑死里初,一個(gè)胖子當(dāng)著我的面吹牛啃勉,可吹牛的內(nèi)容都是我干的忽舟。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼淮阐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼叮阅!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起泣特,我...
    開(kāi)封第一講書(shū)人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤浩姥,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后状您,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體勒叠,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年膏孟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了眯分。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡柒桑,死狀恐怖弊决,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤飘诗,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布与倡,位于F島的核電站,受9級(jí)特大地震影響昆稿,放射性物質(zhì)發(fā)生泄漏纺座。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一貌嫡、第九天 我趴在偏房一處隱蔽的房頂上張望比驻。 院中可真熱鬧,春花似錦岛抄、人聲如沸别惦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)掸掸。三九已至,卻和暖如春蹭秋,著一層夾襖步出監(jiān)牢的瞬間扰付,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工仁讨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留羽莺,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓洞豁,卻偏偏與公主長(zhǎng)得像盐固,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丈挟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361

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