Stock Maximize
Your algorithms have become so good at predicting the market that you now know what the share price of Wooden Orange Toothpicks Inc. (WOT) will be for the next N days.
Each day, you can either buy one share of WOT, sell any number of shares of WOT that you own, or not make any transaction at all. What is the maximum profit you can obtain with an optimum trading strategy?
examples:
input:
prices[5, 3, 2]
prices[1, 2, 100]
prices[1, 3, 1, 2]
output:
0
197
3
解题思路:
一个容易想到的错误思路是,把价格数组当成波峰波谷,每次遇到波峰就卖出前面所有的股票。这样就变成了Best Time to Buy and Sell Stock II了。
这个问题与Best Time to Buy and Sell Stock II不同在于:Best Time to Buy and Sell Stock问题把buy和sell绑定成一个transaction。所以你buy了就必须sell。而这个题更像实际情况,我可以做n次buy操作,只做一次sell操作(价格最高那天卖出)。
例如[1, 3, 1, 100] 我们应该把最后一天前的全部股票买入,然后再在最后一天全部卖出这样可以得到最大收益。而不是在第二天卖出股票。
正确的思路是,假设第i天是最高价格,那么我们应该买入[0 ~ i - 1]所有的股票,在第i天卖出。对[i + 1, n]重新寻找最大值,重复这个操作。
简化就是:从未来像过去遍历,记录当前最大,如果发现prices[i] < max 那么就卖出第i天的股票。反之,更新max。
public long getMaxProfit(long[] prices){
long max = prices[prices.length - 1];
long profit = 0;
/*Traverse from future to past*/
for(int i = prices.length - 2; i >= 0; i--){
if(prices[i] >= max){
max = prices[i];
}else{
profit += max - prices[i];
}
}
return profit;
}
Best Time to Buy and Sell Stock
Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.
解题思路:
因为只允许1次buy和1次sell操作而且必须先买再卖。那么很显然就得找到最便宜的那天买入,在最贵的那天卖出。所以我们需要一个变量来记录最便宜的股票价格,每次遇到比这个价格贵的就计算max profit。遍历完就得到了结果。
public class Solution {
public int maxProfit(int[] prices) {
if(prices == null || prices.length <= 1){
return 0;
}
int min = prices[0];
int profit = 0;
for(int price : prices){
profit = Math.max(profit, price - min);
min = Math.min(min, price);
}
return profit;
}
}
Best Time to Buy and Sell Stock II
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
解题思路:
因为可以做任意次transactions(buy n & sell n)而且buy和sell的操作次数必须相等,所以思路就是“有钱就赚”。只要今天比明天的股票价格便宜,那么就今天买入,明天卖出。
public class Solution {
public int maxProfit(int[] prices) {
if(prices == null || prices.length <= 1){
return 0;
}
int profit = 0;
for(int i = 1; i < prices.length; i++){
int diff = prices[i] - prices[i - 1];
if(diff > 0){
profit += diff;
}
}
return profit;
}
}
Best Time to Buy and Sell Stock III
Say you have an array for which the ith element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most two transactions.
Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
解题思路:
最多只能做2次transactions。我们可以枚举分割线把题目转换成Best Time to Buy and Sell Stock I的问题。假设第i天是分割线,那么问题就变成了在[0 ~ i]做一次transaction取得最大利益,在[i ~ n]做一次transaction取得最大利益。分解成了做2次Time to Buy and Sell Stock I的问题。
这里用两个数组的DP来记录状态。
left[i] 表示从 0 ~ i天 做一次transaction的最大利润。
right[i] 表示从 i ~ n天 做一次transaction的最大利润。
那么结果result = max(left[i] + right[i]) (i : 1 ~ n)
public class Solution {
public int maxProfit(int[] prices) {
if(prices == null || prices.length <= 1){
return 0;
}
int n = prices.length;
int[] left = new int[n];
int[] right = new int[n];
int min = prices[0];
for(int i = 1; i < n; i++){
left[i] = Math.max(left[i - 1], prices[i] - min);
min = Math.min(min, prices[i]);
}
int max = prices[n - 1];
for(int i = n - 2; i >= 0; i--){
right[i] = Math.max(right[i + 1], max - prices[i]);
max = Math.max(max, prices[i]);
}
int profit = 0;
for(int i = 0; i < n; i++){
profit = Math.max(profit, left[i] + right[i]);
}
return profit;
}
}
Best Time to Buy and Sell Stock IV
Say you have an array for which the i-th element is the price of a given stock on day i.
Design an algorithm to find the maximum profit. You may complete at most k transactions.
Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
自己对DP不太擅长。看了下九章算法的答案。九章算法的黄老师很擅长把复杂的问题分解成简单的小问题。
九章的思路:
dp[i][j] 表示前i天做j次transactions取得的最大利益。
profit[m][n] 表示从day m 到 day n做1次transaction取得的最大利益。
所以状态转移方式是: dp[i][j] = dp[m - 1][j - 1] + profit[m][i]。(0 < m < i)
profit[m][n]可以通过Best Time to Buy and Sell Stock I的方式计算出来。
这个答案放Leetcode上运行会超时,因为时间复杂度是O(n ^ 2 + n ^ 3)。我们得枚举所有的profit[i][j]和m。
解题思路:
看了下disscusion,里面有个得票最高的Java的DP答案,思路是和九章是一样的,但是写得很精巧,可能不太好理解。于是研究下把答案写得更加readable。
这里dp[k][i] 表示前i天做k次transactions得到的最大利益。
很简单的一个DP思路就是第i天要么做一次transaction,要么不做。
这里不做transaction有两种情况:1. dp[k][i] = dp[k - 1][i] 2. dp[k][i] = dp[k][i - 1]
精巧的地方发生在在第i天有操作的情况:
dp[i][j] = Max(dp[i - 1][m - 1] + prices[j] - prices[m]) (0 <= m < j)
上面的表达式就是九章的思路,关键我们对这个表达式进行下面的等价变换。
dp[i][j] = prices[j] + Max(dp[i - 1][m - 1] - prices[m]) (0 <= m < j)
我们可以把跟m相关的部分抽出来单独用一个变量进行表示,就是下面答案里面的max_part。
max_part = Max(dp[i - 1][m - 1] - prices[m]) (0 <= m < j)
所以dp[i][j] = prices[j] + max_part
因为我们是自底向上的记录dp[i - 1][m - 1],所以我们没必要重复计算枚举分界线m。这样就少了一层循环。
时间复杂度变成了O(n ^ 2)。
特殊情况是操作次数k大于了天数,那么就变成了Best Time to Buy and Sell Stock II的问题了。
class Solution{
//Optimized answer, time complexity: O(n ^ 2) space complexity: O(KN)
public int maxProfit(int[] prices , int k) {
if(prices == null || prices.length < 2){
return 0;
}
int len = prices.length;
/*Quick solution for special case*/
if(k > len / 2){
int profit = 0;
for(int i = 1; i < len; i++){
int diff = prices[i] - prices[i - 1];
if(diff > 0){
profit += diff;
}
}
return profit;
}
/*dp[i][j] means maximum profit from day 0 to day j with i transactions*/
int[][] dp = new int[k + 1][len];
/*case1: dp[i][j] = dp[i][j - 1] no sell action happen on day j
*case2: dp[i][j] = dp[i - 1][j] no sell action || i transactions are too many
*case3: dp[i][j] = Max(dp[i - 1][m - 1] + prices[j] - prices[m]) (0 <= m < j)
* do one transaction between day m and day j(buy at day m and sell at day j)
* And we can transfer the formula into:
* dp[i][j] = prices[j] + Max(dp[i - 1][m - 1] - prices[m]) (0 <= m < j)
* We can use a variables to express Max(dp[i - 1][m - 1] - prices[m]),
* because we get dp value from bottom to top*/
for(int i = 1; i <= k; i++){
int max_part = dp[i - 1][0] - prices[0];
for(int j = 1; j < len; j++){
/*case 1 & 2*/
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
/*case 3*/
dp[i][j] = Math.max(dp[i][j], max_part + prices[j]);
max_part = Math.max(max_part, dp[i - 1][j - 1] - prices[j]);
}
}
return dp[k][len - 1];
}
}