動態規劃

幾篇很好的資料

動態規劃問題

  • 最長非降子序列
    一個序列有N個數:A[1],A[2],…,A[N],求出最長非降子序列的長度.
  • 股票買賣:一次交易
    你得到一系列在接下來幾天的股票價格,現在你被允許只用一次交易(就是買進再賣出)來獲取最大利益
  • 股票買賣:兩次交易
    允許兩次買賣,但是買之前手里必須沒有股票
  • 股票買賣:多次交易
    允許 k 次買賣,但是買之前手里必須沒有股票
  • 二維矩陣路線
    平面上有N*M個格子,每個格子中放著一定數量的蘋果。你從左上角的格子開始, 每一步只能向下走或是向右走,每次走到一個格子上就把格子里的蘋果收集起來, 這樣下去,你最多能收集到多少個蘋果
  • 網絡節點間最短路徑
    給定網絡graph,和其內指定節點v1 v2,求出v1到v2的帶權重的最短路徑.

基本思想和策略

分析 問題 可能的 子問題,記錄子問題的狀態,通過子問題的狀態轉移函數間接推導 問題 的解.
 以 動態規劃-新手到專家"最長非降子序列"問題為例,進行介紹

int v[] = {5,3,4,8,6,7}
//很明顯v[]的最長非降子序列是 3 4 6 7, 下面給出動態規劃的思考過程
  • 子問題
    求出v[0]~v[i] 的最長非降子序列s(i), 對與s(0)顯然s(0)= {5}
    看看能不能通過s(0)推出 s(1), 然后由s(1),s(0) 推出s(2) ....
seqId LIS analyze
s0 5 default
s1 3 v[1]<s0.max: {3}
s2 3 4 v[2]>s1.max: {3 4} v[2]<s0.max: {4}
s3 3 4 8 v[3]>s2.max: {3 4 8} v[3] > s1.max: {3 8} ...
s4 3 4 6 v[4] > s3.max: {3 4 6} ...
s5 3 4 6 7 v[5] > s4.max: {3 4 6 7} ...
  • 狀態轉移函數
    s(i) = longest{ v[i], s(j), s(k),... }
    其中 0<=k<j< i , 且 v[i]>= s(k).max, v[i] >= s(j).max
    s(0) = { v[0] };
    這樣就可以像上表一樣從s(0)利用狀態轉移函數一步步推導出s[n-1];
  • 總結
  1. 分析子問題; //v[0]~v[i] 的最長非降子序列s(i)

  2. 給出已知子問題的解; // s(0)= {5}

  3. 嘗試推出下一個子問題;


    v[1]<s0.max => s(1) = {3}


    v[2]>s1.max: {3 4}
    v[2]<s0.max: {4} => s(2) = {3,4}


  4. 分析子問題的狀態轉移函數;
    s(i) = longest{ v[i], s(j), s(k),... }
    其中 0<=k<j< i , 且 v[i]>= s(k).max, v[i] >= s(j).max

  5. 整理以上思路即可;

//摘自[動態規劃-新手到專家]
#include <iostream>
using namespace std;

int lis(int A[], int n){
    int *d = new int[n];
    int len = 1;
    for(int i=0; i<n; ++i){
        d[i] = 1;
        for(int j=0; j<i; ++j)
            if(A[j]<=A[i] && d[j]+1>d[i])
                d[i] = d[j] + 1;
        if(d[i]>len) len = d[i];
    }
    delete[] d;
    return len;
}
int main(){
    int A[] = {
        5, 3, 4, 8, 6, 7
    };
    cout<<lis(A, 6)<<endl;
    return 0;
}

股票買賣:兩次交易

牛客網oj:風口上的豬
參考LeetCode題解
問題: 已知n天的股票走勢price[n], 求通過買賣兩次可以獲得的最大利潤,要求是買股票時不能持有股票.
分析:

  • 簡單方法:
    記至進行一次買賣,在0~i天內進行買賣,可獲得的最大收益是benefit(0,i);
    則原問題答案即:max{ benefit(0,i)+benefit(i+1,n) }, 0<=i<n;
    復雜度高為O(n^2);
    benefit(0,n-1)的求解方法如下:
  1. 分析子問題; //在0~i天內進行yic買賣,可獲得的最大收益是benefit(0,i)

  2. 給出已知子問題的解; // benefit(0) = 0; benefit(1) = max{0,price[1]-price[0]};

  3. 嘗試推出下一個子問題;


    minPrice = min{price[1], price[0]} //當前最低價


    benefitTemp = price[2] - minPrice
    //與之前的策略比較,設置當前最大收益
    if benefitTemp > benefit(0,1) : benefit(0,2) = benefitTemp
    else : benefit(0,2) = benefit(0,1) ;
    if benefitTemp < 0 : minPrice = price[0,2];//更新當前最低價


  4. 分析子問題的狀態轉移函數;
    minPrice = min { price[0],...,price[i]);
    benefit(0,i) = max{ price[2] - minPrice, benefit( 0, i - 1 ) };

  • 雙向求解:
    上方法有重復求解的問題,可以通過2次遍歷來解決
    1. 遍歷求解在i天及之前進行一次買賣,可獲得的最大收益 benefit(0,i), 0<=i<n;
    2. 遍歷求解在i天及之后進行一次買賣,可獲得的最大收益 benefit(i,n-1), 0<=i<n;
    3. 遍歷 benefit(0,i)+ benefit(i+1,n-1), 即可求出最大收益
class Solution {
    vector<int> saleMax;
    vector<int> buyMax;
public:
    Solution(){
        saleMax.reserve(100);
        buyMax.reserve(100);
    }
    /**
     * 計算你能獲得的最大收益
     * 
     * @param prices Prices[i]即第i天的股價
     * @return 整型
     */
    int calculateMax(vector<int> prices) {
        int len = prices.size();
        if(len < 2) return 0;
        int temp(0),minPrice(0),maxPrice(0);
        //第i天及之前買賣可以獲得的最大利益
        saleMax.resize(len);
        saleMax[0]=0;
        minPrice = prices[0];
        for(int i=1;i<len;i++) {
            temp = prices[i]-minPrice;
            minPrice = (temp<0)?prices[i]:minPrice;
            saleMax[i] = (temp>0)?temp:0;
        }
        
        //從第i天可以買入,則可以獲得的最大利益
        int maxBenifit(0);
        buyMax.resize(len);
        maxPrice = prices[len-1];
        for(int i=len-1;i>=0;i--){
            temp = maxPrice-prices[i];
            maxPrice = (temp<0)?prices[i]:maxPrice;
            maxBenifit = (temp>maxBenifit)?temp:maxBenifit;
            buyMax[i] = maxBenifit;
        }
        maxBenifit = 0;
        for(int i=0;i<len;i++) {
            temp = saleMax[i]+buyMax[i];
            maxBenifit = (temp>maxBenifit)?temp:maxBenifit;
        }
        return maxBenifit;
    }
};

其他問題的代碼

  • 二維矩陣路線選擇
     問題:平面上有N*M個格子,每個格子中放著一定數量的蘋果。你從左上角的格子開始, 每一步只能向下走或是向右走,每次走到一個格子上就把格子里的蘋果收集起來,這樣下去,你最多能收集到多少個蘋果。
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;

const unsigned N = 4;
const unsigned M = 8;

int main() {
    //(0,0)->(2,0)->(2,5)->(3,5)->(3,7)
    // 54; 
    unsigned apple[N*M] = {
        1, 3, 2, 1, 3, 5, 8, 9,
        4, 3, 1, 5, 2, 1, 1, 1,
        3, 6, 1, 7, 9, 2, 1, 1,
        6, 1, 1, 1, 1, 5, 7, 9
    };
    unsigned maxNum[N*M];
    maxNum[0]=apple[0];
    queue<unsigned> qu;
    qu.push(0);

    while (!qu.empty() ) {
        unsigned curIndex = qu.front();
        qu.pop();
        unsigned n = curIndex/M;    // (0,0) 0/M = 0; (0,7) 7/M = 0; (1,0) 8/M = 1 (1,7) 15/8 = 1
        unsigned m = curIndex%M;    // (0,0) 0%M = 0; (0,7) 7/M = 0; (1,0) 8/M = 0 (1,7) 15%8 = 7;
        if(m<M-1) {
            int r = curIndex+1;
            unsigned upOne = (n==0)?0 :maxNum[r-M];
            maxNum[r] = apple[r] + max<unsigned>(maxNum[curIndex], upOne);
            qu.push(r);
        }
        if(m==0&&n<N-1) {
            int r = curIndex+M;
            maxNum[r] = maxNum[curIndex] + apple[r];
            qu.push(r);
        }
    }
    cout <<maxNum[N*M-1]<<endl;
    return 1;
}
小鵝雙拼.jpg
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,327評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,996評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,316評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,406評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,128評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,524評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,576評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,759評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,310評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,065評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,249評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,821評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,479評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,909評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,140評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,984評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,228評論 2 375

推薦閱讀更多精彩內容

  • 本文翻譯自TopCoder上的一篇文章: Dynamic Programming: From novice to ...
    扎Zn了老Fe閱讀 1,931評論 0 3
  • 回溯算法 回溯法:也稱為試探法,它并不考慮問題規模的大小,而是從問題的最明顯的最小規模開始逐步求解出可能的答案,并...
    fredal閱讀 13,707評論 0 89
  • 分治方法 將問題劃分成互不相交的子問題 遞歸地求解子問題 將子問題的解組合起來 動態規劃(兩個要素:最優子結構、子...
    superlj666閱讀 513評論 0 0
  • 1.不知道為什么,明明點了蚊香,卻依然被叮了好多下,莫非蚊子已經對蚊香免疫了?都說秋天的蚊子特別厲害,俗稱“秋老虎...
    塵世書童閱讀 197評論 0 1
  • 鴻記煌三汁燜鍋浪漫之旅!蘆笙向來就是一個敏感的人,可能是高考的壓力過大或者說是自己自小跟著爺爺奶奶,父母工作繁忙沒...
    未聞風音閱讀 203評論 0 0