动态规划

作者: 秋水涟漪 | 来源:发表于2016-08-29 14:27 被阅读0次

    几篇很好的资料

    动态规划问题

    • 最长非降子序列
      一个序列有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

    相关文章

      网友评论

        本文标题:动态规划

        本文链接:https://www.haomeiwen.com/subject/uhixettx.html