此处的三个题跟Maximum Subarray,可以先看此题
I)
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.
此题的第一个,动态规划算法。状态转移方程f(i) = Max(maxProfit, prices[i]-minPrice)
public int maxProfit(int[] prices) {
int maxPro = 0;
int minPrice = Integer.MAX_VALUE;
for(int i = 0; i < prices.length; i++){
minPrice = Math.min(minPrice, prices[i]);
maxPro = Math.max(maxPro, prices[i] - minPrice);
}
return maxPro;
}
算法从前向后遍历这个数组,minPrice是指prices[0...i]的最小值,maxPro是指在prices[0...i]的最大利润
当然也可以从后向前遍历,如下:
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0) return 0;
int max = prices[prices.length-1];
int maxProfit = 0;
for (int i = prices.length-1; i >= 0; i--) {
max = Math.max(max, prices[i]);
maxProfit = Math.max(maxProfit, max-prices[i]);
}
return maxProfit;
}
max是指prices[i...n-1]的最大值,maxPro是指在prices[i...n-1]的最大利润
还有一种解法,如下:
public int maxProfit(int[] prices) {
int maxPro = 0;
int maxCur = 0;
for(int i = 1; i < prices.length; i++){
maxCur = Math.max(0, maxCur += prices[i] - prices[i-1]);
maxPro = Math.max(maxPro, maxCur);
}
return maxPro;
}
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).
第二题,没有限制次数,很显然贪心算法,如下:
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0) return 0;
int max = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i]-prices[i-1] > 0) max += prices[i]-prices[i-1];
}
return max;
}
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).
第三题的限制为最多交易两次,所以我们就可以根据第一题。把此题分解为在i之前交易一次,在i之后交易一次。状态转移方程为:
f(2, 0, n) = Max{ f(1, 0, i)+f(1, i, n) } i = 1, 2, 3...n 其中f(k, m, n)表示交易在prices[m...n]直接交易k次的最大利润,代码如下:
public int maxProfit1(int[] prices) {
if (prices == null || prices.length == 0) return 0;
int max = 0;
for (int i = 0; i < prices.length; i++) {
int curProfit = maxProfit1(prices, 0, i) + maxProfit1(prices, i, prices.length);
max = Math.max(max, curProfit);
}
return max;
}
private int maxProfit1(int[] prices, int s, int e) {
if (s > e) return 0;
int min = prices[s];
int maxProfit = 0;
for (int i = s; i < e; i++) {
min = Math.min(min, prices[i]);
maxProfit = Math.max(maxProfit, prices[i]-min);
}
return maxProfit;
}
代码超时,因为有重复子问题。所以我们可以根据问题I,将结果保存在数组中,之后在查询。
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0) return 0;
int max = 0;
//以i为结尾的最大利润,即[0...i]
int[] maxEH = maxEndingHere(prices);
//以i为起始的最大利润,即[i...n]
int[] maxBH = maxBeginingHere(prices);
for (int i = 0; i < prices.length; i++) {
int curProfit = maxEH[i]+maxBH[i];
max = Math.max(max, curProfit);
}
return max;
}
private int[] maxEndingHere(int[] prices) {
int[] ret = new int[prices.length];
int min = prices[0];
for (int i = 1; i < prices.length; i++) {
min = Math.min(min, prices[i]);
ret[i] = Math.max(ret[i-1], prices[i]-min);
}
return ret;
}
private int[] maxBeginingHere(int[] prices) {
int[] ret = new int[prices.length];
int max = prices[prices.length-1];
for (int i = prices.length-1; i > 0; i--) {
max = Math.max(max, prices[i]);
ret[i] = Math.max(ret[i-1], max-prices[i]);
}
return ret;
}
当然可以把代码中的三个循环合并为两个。此题还有其他解法,一会在讲解。
IV)
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 k transactions.
Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
f(k, i) = max{ f(k, i-1), max { f(k-1, j-1)+prices[i]-prices[j] } } j = 0, 1, 2...i-1
f(0, i) = 0, f(k, 0) = 0
其中f(k,i)是指在用prices[0...i],k次交易所得到的最大利润,分析状态转移方程,对于prices[i]来说,有两种选择:
1)不参加,则问题退化为在k次交易的前提下,只用prices[0...i-1],即f(k, i-)
2)参加交易,则此时i必须为出售的价格,因为这是交易的最后一天。所以有prices[i]-prices[j] + f(k-1, j-1) j = 0. 1. i-1找最大值
对上述问题求最大值。
代码如下:
public int maxProfit(int k, int[] prices) {
if (prices == null || prices.length == 0) return 0;
if (k >= prices.length/2) {
//退化成问题II
return maxProfit(prices);
}
//f(k, i) = max{ f(k, i-1), max { f(k-1, j-1)+prices[i]-prices[j] } } j = 0, 1, 2...i-1
//f(0, i) = 0, f(k, 0) = 0
//f(k, i) = max{ f(k, i-1), prices[i]+max { f(k-1, j-1)-prices[j] } } j = 0, 1, 2...i-1
//令tempMax = max { f(k-1, j-1)-prices[j] } j = 0, 1, 2...i-1
int[][] dp = new int[k+1][prices.length];
for (int i = 1; i <= k; i++) {
int tempMax = -prices[0];
for (int j = 1; j < prices.length; j++) {
dp[i][j] = Math.max(dp[i][j-1], tempMax+prices[j]);
tempMax = Math.max(tempMax, dp[i-1][j-1]-prices[j]);
}
}
return dp[k][prices.length-1];
}
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0) return 0;
int max = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i]-prices[i-1] > 0) max += prices[i]-prices[i-1];
}
return max;
}
其中,tempMax是指,在进行i-1次交易,并且只用前j-1的prices,并且此时已经买了price[j]的最大值。而内层循环一直在寻找进行了i-1次交易,并且买了prices[j]的股票的最大值。tempMax+prices[j]是指之前已经买了股票,现在要在prices[j]出售
有了上述解法,问题三也有了新的解法,即k=2。但是还有更简单的表述方式。如下:
public int maxProfit(int[] prices) {
int hold1 = Integer.MIN_VALUE, hold2 = Integer.MIN_VALUE;
int release1 = 0, release2 = 0;
for(int i:prices){ // Assume we only have 0 money at first
release2 = Math.max(release2, hold2+i); // The maximum if we've just sold 2nd stock so far.
hold2 = Math.max(hold2, release1-i); // The maximum if we've just buy 2nd stock so far.
release1 = Math.max(release1, hold1+i); // The maximum if we've just sold 1nd stock so far.
hold1 = Math.max(hold1, -i); // The maximum if we've just buy 1st stock so far.
}
return release2; ///Since release1 is initiated as 0, so release2 will always higher than release1.
}
其实也是基于上述递推公式的。