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).
A: 算法2更好, 在最下面
------------------------3 rd pass -------------------
以前脑子不清醒。 这个明明就是2次分开的profit的情况。
也就是说,可以先找出最赚钱的那个情况,然后,再 找出左右两边的subarray中最赚钱的情况。然后二者相加即可。
嗯,还是算法2, 左右扫,更好些。
divide and conquer
设i从0到n-1,那么针对每一个i,看看在prices的子序列[0,...,i][i+1,...,n-1]上分别取得的最大利润(第一题)即可。
这样初步一算,时间复杂度是O(n2)。
经过和只买一次的情况对比,我们发现:
Idea:
分两种情况:
1: 第二个max和只有一个transaction时的数组(max1) 分离的。
最新的一遍,只考虑了这一种情况,大谬 !!!!!!!!
2: max2与 max1有交际-----
public class Solution {
public static final int minValue = -100000;
public int maxProfit(int[] prices) {
// This is max profit for two transactions
if (prices != null && prices.length <= 1) {
return 0;
}
int[] change = new int[prices.length - 1];
for (int i = 0; i < prices.length - 1; i++) {
change[i] = prices[i + 1] - prices[i];
}
// return maxChange(change, 0, change.length - 1)[2];
// case 1:
// when the 2nd transaction independent of the 1st transaction
// i.e. they are disjoint
int[] max1 = maxChange(change, 0, change.length - 1);
// exclude the intervals from changes , to do second max
if(max1[2]<0){
return 0; // if the max buying profit in a single transaction is < 0
}
int[] modifiedChanges = new int[change.length];
for (int i = 0; i < change.length; i++) {
if (i >= max1[0] && i <= max1[1]) {
modifiedChanges[i] = minValue;
} else {
modifiedChanges[i] = change[i];
}
}
int[] max2 = maxChange(modifiedChanges, 0, change.length - 1);
//since when change = [1] only, the max 2 would return the above min_value;
int disJointMax = max1[2] + Math.max( max2[2],0); // we can reasonbalely assume that max2[2]>0
// Case 2:
// when two transactions are both intersect with the interval of one
// transaction
// we know that left and right terminal belongs to the two transactions.
// but they finally divided by the negative interval inside max1
// First we modify the changes.
for (int i = 0; i < change.length; i++) {
modifiedChanges[i] = -change[i];
}
int[] minInside = maxChange(modifiedChanges, max1[0], max1[1]);
if (minInside[0] - 1 < 0 || minInside[1] + 1 > change.length - 1) {
// actually this is the case 1
return disJointMax;
}
int[] maxLeft = maxChange(change, 0, minInside[0] - 1);
int[] maxRight = maxChange(change, minInside[1] + 1, change.length - 1);
int intersectMax = maxLeft[2] + maxRight[2];
if (disJointMax > intersectMax) {
return Math.max(disJointMax, 0);
} else {
return Math.max(intersectMax, 0);
}
}
// divide and conquere problem.
public int[] maxChange(int[] change, int start, int end) {
if (start == end) {
int[] tmp = { start, end, change[start] };
return tmp;
} else {
int middle = (int) ((start + end) / 2);
int[] leftMax = maxChange(change, start, middle);
int[] rightMax = maxChange(change, middle + 1, end);
int[] crossMax = maxCross(change, start, middle, end);
if (leftMax[2] >= rightMax[2] && leftMax[2] >= crossMax[2])
return leftMax;
if (rightMax[2] >= leftMax[2] && rightMax[2] >= crossMax[2])
return rightMax;
else
// (crossMax[2] >= leftMax[2] && crossMax[2] >= rightMax[2])
return crossMax;
}
}
private int[] maxCross(int[] change, int start, int middle, int end) {
// get max left part
int leftMax = 0;
int sum = 0;
int leftIndex = middle;
for (int i = middle; i >= start; i--) {
sum += change[i];
if (sum > leftMax) {
leftMax = sum;
leftIndex = i;
}
}
int rightMax = 0;
int rightIndex = middle + 1;
sum = 0;
for (int i = middle + 1; i <= end; i++) {
sum += change[i];
if (sum > rightMax) {
rightMax = sum;
rightIndex = i;
}
}
int[] tmp = { leftIndex, rightIndex, leftMax + rightMax };
return tmp;
}
}
Mistakes:
1: 没有考虑,可以有不买的选择,因此,当price =[2,1]时,输出了-1,而不是0.
Learned:
1:上面犯了一个错误。
原因在于,java的最小最大值如何运算,没搞清楚。看下面
System.out.println(Integer.MIN_VALUE); System.out.println(Integer.MIN_VALUE-1); 打印的结果为: -2147483648 2147483647
---------------------------第二遍, 有新解法----------------------------
思路来源于 这里
哎,其实,本题目的题意是这样的:
某一天买进, 再后面的一天卖出,这样就是利润。
因此,也只需要, 找到前面的一天最low的, 再找到后面的一天最high的 , 就可以了。
问题1,之所以要用divide and conquere ,就是解决O(n)的问题,而且用了O(lg(n))的空间复杂度。
现在,我们放开, 就可以用O(n)的空间复杂度+ 时间复杂度 解决问题1.。。 DP思想。
那么,本问题III, 也可以用 类似的思想解决。
改进的方法就是动态规划了,那就是第一步扫描,先计算出子序列[0,...,i]中的最大利润,用一个数组保存下来,那么时间是O(n)。
第二步是逆向扫描,计算子序列[i,...,n-1]上的最大利润,这一步同时就能结合上一步的结果计算最终的最大利润了,这一步也是O(n)。
所以最后算法的复杂度就是O(n)的。
public class Solution {
public int maxProfit(int[] prices) {
if(prices.length <= 1 )
return 0;
int n = prices.length, minBefore = prices[0];
int maxProfitBefore[] = new int[n];
for(int i =1;i<n;i++){
maxProfitBefore[i] = Math.max(maxProfitBefore[i-1],prices[i] - minBefore);
minBefore = Math.min(minBefore, prices[i] );
}
// calculate last
int result = 0, maxAfter = prices[n-1];
for(int i=n-2;i>=0;i--){
result = Math.max(result, maxProfitBefore[i] + maxAfter - prices[i]);
maxAfter = Math.max(maxAfter, prices[i]);
}
return result;
}
}

No comments:
Post a Comment