动态规划

几篇很好的资料

  • 动态规划:新手到专家
  • data structures and algorithms
  • LeetCode题解
  • 股票买卖最大收益问题

动态规划问题

  • 最长非降子序列
    一个序列有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]
s2 3 4 v[2]>s1.max: {3 4} v[2]
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= 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] s(1) = {3}


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


  4. 分析子问题的状态转移函数;
    s(i) = longest{ v[i], s(j), s(k),... }
    其中 0<=k= s(k).max, v[i] >= s(j).max

  5. 整理以上思路即可;

//摘自[动态规划-新手到专家]
#include 
using namespace std;

int lis(int A[], int n){
    int *d = new int[n];
    int len = 1;
    for(int i=0; id[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<

股票买卖:两次交易

牛客网oj:风口上的猪
参考LeetCode题解
问题: 已知n天的股票走势price[n], 求通过买卖两次可以获得的最大利润,要求是买股票时不能持有股票.
分析:

  • 简单方法:
    记至进行一次买卖,在0~i天内进行买卖,可获得的最大收益是benefit(0,i);
    则原问题答案即:max{ benefit(0,i)+benefit(i+1,n) }, 0<=i 复杂度高为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
    2. 遍历求解在i天及之后进行一次买卖,可获得的最大收益 benefit(i,n-1), 0<=i
    3. 遍历 benefit(0,i)+ benefit(i+1,n-1), 即可求出最大收益
class Solution {
    vector saleMax;
    vector buyMax;
public:
    Solution(){
        saleMax.reserve(100);
        buyMax.reserve(100);
    }
    /**
     * 计算你能获得的最大收益
     * 
     * @param prices Prices[i]即第i天的股价
     * @return 整型
     */
    int calculateMax(vector 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;i0)?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;imaxBenifit)?temp:maxBenifit;
        }
        return maxBenifit;
    }
};

其他问题的代码

  • 二维矩阵路线选择
     问题:平面上有N*M个格子,每个格子中放着一定数量的苹果。你从左上角的格子开始, 每一步只能向下走或是向右走,每次走到一个格子上就把格子里的苹果收集起来,这样下去,你最多能收集到多少个苹果。
#include 
#include 
#include 
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 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(maxNum[curIndex], upOne);
            qu.push(r);
        }
        if(m==0&&n
动态规划_第1张图片
小鹅双拼.jpg

你可能感兴趣的:(动态规划)