2018-04-19 19:28:21

股票问题是leetcode里一条非常经典的题目,因为其具有一定的现实意义,所以还是在数学建模方面还是有很多用武之地的。这里会对stock的给出一个比较通用的解法,然后会针对各个细分问题用通解去解决,主要采用的算法是动态规划算法。

问题描述:Given an array representing the price of stock on each day, what determines the maximum profit we can obtain?

问题求解:首先很容易想到的是最大收入和具体结算时间以及这些天里的最多交易笔数有关,因此我们可以定义一个数组T[i][k],表示在第i天结束时,最多经过k笔交易所产生的最大收入。很显然的,我们可以得到一个初始条件T[-1][k] = T[i][0] = 0,也就是说在第-1天,无论经过多少笔交易都没有收益,在第i天,如果最多为0次交易,那么也将不会产生收益。根据动态规划的一般思路,我们现在需要做的就是建立T[i][k]和T[i-1][k], T[i][k-1], T[i-1][k-1], ...之间的关系下面将介绍如何建立这种关系。

对于第i天的交易,我们可以采取的策略有sell,buy,rest。我们当然在最初的时候并不知道哪个选择是最优的,所以我们需要去判断一下哪个选择对于我们来说能得到一个好的结果。由于有每次只能手持一股的限制,所以我们无法在已经买过股票的情况下再购买股票,同时也无法在已经卖空股票的情况下再继续卖股票,因此,我们需要一个变量来揭示第i天结束时,手里剩余的股票数量。

因此T[i][k]的定义需要被拆分成两个:T[i][k][0] and T[i][k][1]。并且我们还可以得到如下的初始条件和递推关系:

  1. Base cases:
    T[-1][k][0] = 0, T[-1][k][1] = -Infinity
    T[i][0][0] = 0, T[i][0][1] = -Infinity

  2. Recurrence relations:
    T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
    T[i][k][1] = max(T[i-1][k][1], T[i-1][k-1][0] - prices[i])

注意:定义一次交易为买入的时候就算作一笔交易,不可等到卖出才算作交易。

  • 121. Best Time to Buy and Sell Stock

问题描述:

问题求解:

k = 1,则递推式变为:

T[i][1][0] = max(T[i - 1][1][0],T[i - 1][1][1] + prices[i])

T[i][1][1] = max(T[i - 1][1][1],0 - prices[i])

对空间复杂度进行优化,可以在<O(n),O(1)>完成求解。

    public int maxProfit(int[] prices) {
int Ti10 = 0;
int Ti11 = Integer.MIN_VALUE;
for (int price : prices) {
Ti10 = Math.max(Ti10, Ti11 + price);
Ti11 = Math.max(Ti11, -price);
}
return Math.max(Ti10, Ti11);
}
  • 122. Best Time to Buy and Sell Stock II

问题描述:

问题求解:

k = INF,则递推式变为:

T[i][k][0] = max(T[i - 1][k][0],T[i - 1][k][1] + prices[i])

T[i][k][1] = max(T[i - 1][k][1],T[i - 1][k][0] - prices[i])

对空间复杂度进行优化,可以在<O(n),O(1)>完成求解。

    public int maxProfit(int[] prices) {
int Tik0 = 0;
int Tik1 = Integer.MIN_VALUE;
for (int price : prices) {
int tmp = Tik0;
Tik0 = Math.max(Tik0, Tik1 + price);
Tik1 = Math.max(Tik1, tmp - price);
}
return Math.max(Tik0, Tik1);
}
  • 714. Best Time to Buy and Sell Stock with Transaction Fee

问题描述:

问题求解:

k = INF with fee,则递推式变为:

T[i][k][0] = max(T[i - 1][k][0],T[i - 1][k][1] + prices[i])

T[i][k][1] = max(T[i - 1][k][1],T[i - 1][k][0] - prices[i] - fee)

对空间复杂度进行优化,可以在<O(n),O(1)>完成求解。

    public int maxProfit(int[] prices, int fee) {
int Tik0 = 0;
int Tik1 = Integer.MIN_VALUE;
for (int i = 0; i < prices.length; i++) {
int tmp = Tik0;
Tik0 = Math.max(Tik0, Tik1 + prices[i]);
Tik1 = Math.max(Tik1, tmp - prices[i] - fee);
}
return Tik0;
}
  • 309. Best Time to Buy and Sell Stock with Cooldown

问题描述:

问题求解:

k = INF with cooldown,则递推式变为:

T[i][k][0] = max(T[i - 1][k][0],T[i - 1][k][1] + prices[i])

T[i][k][1] = max(T[i - 1][k][1],T[i - 2][k][0] - prices[i] )

对空间复杂度进行优化,可以在<O(n),O(1)>完成求解。

    public int maxProfit(int[] prices) {
int Tik0 = 0;
int Tik1 = Integer.MIN_VALUE;
int prev = 0;
for (int price : prices) {
int tmp = Tik0;
Tik0 = Math.max(Tik0, Tik1 + price);
Tik1 = Math.max(Tik1, prev - price); // T[i - 1][k][0] 若是 rest,则T[i - 1][k][0] = T[i - 2][k][0],若卖出,则对于第 i 天 buy 只能选择T[i - 2][k][0]
prev = tmp;
}
return Math.max(Tik0, Tik1);
}
  • 123. Best Time to Buy and Sell Stock III

问题描述:

问题求解:

k = 2,则递推式变为:

T[i][2][0] = max(T[i - 1][2][0],T[i - 1][2][1] + prices[i])

T[i][2][1] = max(T[i - 1][2][1],T[i - 1][1][0] - prices[i])

T[i][1][0] = max(T[i - 1][1][0],T[i - 1][1][1] + prices[i])

T[i][1][1] = max(T[i - 1][1][1],0 - prices[i])

对空间复杂度进行优化,可以在<O(n),O(1)>完成求解。

    public int maxProfit(int[] prices) {
int Ti20 = 0;
int Ti21 = Integer.MIN_VALUE;
int Ti10 = 0;
int Ti11 = Integer.MIN_VALUE;
for (int price : prices) {
Ti20 = Math.max(Ti20, Ti21 + price);
Ti21 = Math.max(Ti21, Ti10 - price);
Ti10 = Math.max(Ti10, Ti11 + price);
Ti11 = Math.max(Ti11, -price);
}
return Math.max(Ti20, Ti21);
}
  • 188. Best Time to Buy and Sell Stock IV

问题描述:

问题求解:

回到最初讨论的问题,k是一个有限常数,则递推式为:

T[i][k][0] = max(T[i - 1][k][0],T[i - 1][k][1] + prices[i])

T[i][k][1] = max(T[i - 1][k][1],T[i - 1][k - 1][0] - prices[i])

需要注意到,当k > n / 2的时候,k就可以看作是INF,因为同一天卖出买入等效于这一天没有操作,所以最多的k次有效交易为n / 2,超过了这个数就有废操作了。

另外,此时每一天有k个状态,所以可以用两个k + 1大小的数组来保存状态量,最终在<O(kn),O(k)>完成求解。

    public int maxProfit(int k, int[] prices) {
if (k > prices.length >>> 1) {
int Tik0 = 0;
int Tik1 = Integer.MIN_VALUE;
for (int price : prices) {
int tmp = Tik0;
Tik0 = Math.max(Tik0, Tik1 + price);
Tik1 = Math.max(Tik1, tmp - price);
}
return Math.max(Tik0, Tik1);
} int[] Tik0 = new int[k + 1];
int[] Tik1 = new int[k + 1];
Arrays.fill(Tik1, Integer.MIN_VALUE);
for (int price : prices) {
for (int i = k; i > 0; i--) {
Tik0[i] = Math.max(Tik0[i], Tik1[i] + price);
Tik1[i] = Math.max(Tik1[i], Tik0[i - 1] - price);
}
}
return Math.max(Tik0[k], Tik1[k]);
}

本题如果使用原始的递推式来解的话可以这么修改,因为-1在数组中是没有办法进行初始化的,于是我们可以这样定义递推公式:

dp[i][k][0] : 经过了i天并使用了k次交易后剩余0股的最大利润;

dp[i][k][1] : 经过了i天并使用了k次交易后剩余1股的最大利润;

于是dp[0][k][0] = 0;dp[0][k][1] = Integer.MIN_VALUE,这样就可以避免上述的问题。

    public int maxProfit(int k, int[] prices) {
int n = prices.length;
if (k >= n / 2) return helper(prices);
int[][][] dp = new int[k + 1][n + 1][2];
for (int i = 0; i <= n; i++) {
dp[0][i][0] = 0;
dp[0][i][1] = Integer.MIN_VALUE;
}
for (int i = 0; i <= k; i++) {
dp[i][0][0] = 0;
dp[i][0][1] = Integer.MIN_VALUE;
}
for (int i = 1; i <= k; i++) {
for (int j = 1; j <= n; j++) {
dp[i][j][0] = Math.max(dp[i][j - 1][0], dp[i][j - 1][1] + prices[j - 1]);
dp[i][j][1] = Math.max(dp[i][j - 1][1], dp[i - 1][j - 1][0] - prices[j - 1]);
}
}
return dp[k][n][0];
} private int helper(int[] prices) {
int tik0 = 0;
int tik1 = Integer.MIN_VALUE;
for (int price : prices) {
int tmp = tik0;
tik0 = Math.max(tik0, tik1 + price);
tik1 = Math.max(tik1, tmp - price);
}
return tik0;
}

【注意】

这次在解决这个问题的时候出现了MLE的情况,原本以为是三维的数组超了内存,但是实际是没有对k的大小进行判断。

所以这里对k的大小进行提前判断是必要的,如果不判断,直接使用dp那么就会MLE。

这里还有一个问题需要考虑,就是一般来说,我们会将问题规约到可以解决的更小的问题上,而且一般是对k进行外层遍历。

换言之,就是和问题给的方式相同,我们先算k次交易各个天数的最大结果,之后下一轮计算k + 1次的,这里没有这么计算的原因很简单,就是在递推式中第i天的交易总额和第i - 1天的k + 1次交易也有关联,也就是在子问题中k的规模并没有更小,所以必须针对每一天算出所有的k后在计算下一天。

    public int maxProfit(int k, int[] prices) {
int days = prices.length;
if (k >= days / 2) return helper(prices);
int[][] dp = new int[k + 1][2];
for (int i = 0; i <= k; i++) {
dp[i][0] = 0;
dp[i][1] = Integer.MIN_VALUE;
}
for (int price : prices) {
for (int i = 1; i <= k; i++) {
dp[i][0] = Math.max(dp[i][0], dp[i][1] + price);
dp[i][1] = Math.max(dp[i][1], dp[i - 1][0] - price);
}
}
return Math.max(dp[k][0], dp[k][1]);
} private int helper(int[] prices) {
int Tik0 = 0;
int Tik1 = Integer.MIN_VALUE;
for (int price : prices) {
Tik0 = Math.max(Tik0, Tik1 + price);
Tik1 = Math.max(Tik1, Tik0 - price);
}
return Math.max(Tik0, Tik1);
}

  

动态规划-Stock Problem的更多相关文章

  1. 二维剪板机下料问题(2-D Guillotine Cutting Stock Problem) 的混合整数规划精确求解——数学规划的计算智能特征

    二维剪板机下料问题(2-D Guillotine Cutting Stock Problem) 的混合整数规划精确求解——数学规划的计算智能特征 二维剪板机下料(2D-GCSP) 的混合整数规划是最优 ...

  2. LeetCode 309. Best Time to Buy and Sell Stock with Cooldown (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  3. LeetCode 188. Best Time to Buy and Sell Stock IV (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  4. LeetCode 123. Best Time to Buy and Sell Stock III (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  5. 精帖转载(关于stock problem)

    Note: this is a repost(重新投寄) of my original post here with updated solutions(解决方案) for this problem ...

  6. LeetCode 122. Best Time to Buy and Sell Stock II (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  7. LeetCode 121. Best Time to Buy and Sell Stock (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. If you were ...

  8. Codeforces Round #598 (Div. 3)- E. Yet Another Division Into Teams - 动态规划

    Codeforces Round #598 (Div. 3)- E. Yet Another Division Into Teams - 动态规划 [Problem Description] 给你\( ...

  9. 2019-ACM-ICPC-南京区网络赛-D. Robots-DAG图上概率动态规划

    2019-ACM-ICPC-南京区网络赛-D. Robots-DAG图上概率动态规划 [Problem Description] ​ 有向无环图中,有个机器人从\(1\)号节点出发,每天等概率的走到下 ...

随机推荐

  1. 310实验室(七)OptimizationTemplateLibrary

    利用泛型编程思想,C++模板. 首先定义变量或者重新typedef variables: 模板中的变量:_TRandom.double  _TReal._TProblem::TDecision _TD ...

  2. JS通过正则限制 input 输入框只能输入整数、小数(金额或者现金)

    第一: 限制只能是整数 <input type = "text" name= "number" id = 'number' onkeyup= " ...

  3. [转] mysql数据库分离和附加

    1.导出整个数据库 mysqldump -u 用户名 -p 数据库名 > 导出的文件名 mysqldump -u wcnc -p smgp_apps_wcnc > wcnc.sql 2.导 ...

  4. ZOJ 3212 K-Nice

    K-Nice Time Limit: 1 Second      Memory Limit: 32768 KB      Special Judge This is a super simple pr ...

  5. poj1821 Fence【队列优化线性DP】

    Fence Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 6122   Accepted: 1972 Description ...

  6. 您好,前端使用https,后端使用https是会有冲突的情况,所以默认后端都是http 负载均衡即可管理证书,不需要在后端ECS上绑定证书。

    您前端使用https,那么前端就是加密的,后端使用https就是会访问出现问题的,目前阿里云负载均衡默认的配置前端使用https,后端默认就是http,也是无法更改的. 前端使用https,目前只有一 ...

  7. Fast R-CNN论文详解 - CSDN博客

    废话不多说,上车吧,少年 paper链接:Fast R-CNN &创新点 规避R-CNN中冗余的特征提取操作,只对整张图像全区域进行一次特征提取: 用RoI pooling层取代最后一层max ...

  8. SQL基础--查询之四--集合查询

    SQL基础--查询之四--集合查询

  9. Google I/O 2014 - Keynote for Android

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/jingxia2008/article/details/34809467 Google I/O 201 ...

  10. king 选 太子

    king 选 太子 时间限制:3000 ms  |  内存限制:65535 KB 难度:1   描述 啊,从前有一个国家.此国兵强马壮,但是国王却身体不好.于是就想挑一位太子出来: 但是问题来了,国王 ...