记——Best_Time_to_Buy_and_Sell_Stock系列的题中三四和带休息的都不会,留着以后慢慢研究。
第一题:Best Time to Buy and Sell Stock
https://leetcode.com/problems/best-time-to-buy-and-sell-stock/
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.empty())return 0;
int minn = prices[0];
int sum = 0;
for(int i=1;i<prices.size();++i){
if(minn > prices[i]){
minn = prices[i];
continue;
}else{
sum = max(sum,prices[i]-minn);
}
}
return sum;
}
};
第二题:Best Time to Buy and Sell Stock II
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/
class Solution {
public:
int maxProfit(vector<int>& prices) {
vector<int> iv(prices.size(),0);
int i=1;
for(;i<prices.size();++i){
iv[i] = max(iv[i-1],prices[i]-prices[i-1]);
}
return iv[i-1];
}
};
//也可以不用DP
class Solution {
public:
int maxProfit(vector<int> &prices) {
int ret = 0;
for (size_t p = 1; p < prices.size(); ++p)
ret += max(prices[p] - prices[p - 1], 0);
return ret;
}
};
第三题:Best Time to Buy and Sell Stock III
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/
/*
动态规划法。以第i天为分界线,计算第i天之前进行一次交易
的最大收益preProfit[i],和第i天之后进行一次交易的最大
收益postProfit[i],最后遍历一遍,
max{preProfit[i] + postProfit[i]} (0≤i≤n-1)就是最大收益。
第i天之前和第i天之后进行一次的最大收益求法同
Best Time to Buy and Sell Stock I。
*/
public class Solution {
public int maxProfit(int[] prices) {
if (prices.length < 2) return 0;
int n = prices.length;
int[] preProfit = new int[n];
int[] postProfit = new int[n];
int curMin = prices[0];
for (int i = 1; i < n; i++) {
curMin = Math.min(curMin, prices[i]);
preProfit[i] = Math.max(preProfit[i - 1], prices[i] - curMin);
}
int curMax = prices[n - 1];
for (int i = n - 2; i >= 0; i--) {
curMax = Math.max(curMax, prices[i]);
postProfit[i] = Math.max(postProfit[i + 1], curMax - prices[i]);
}
int maxProfit = 0;
for (int i = 0; i < n; i++) {
maxProfit = Math.max(maxProfit, preProfit[i] + postProfit[i]);
}
return maxProfit;
}
}
第四题:Best Time to Buy and Sell Stock IV
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/
/*递推式依然是
local[i][j]=max(global[i-1][j-1]+max(diff,0),local[i-1][j]+diff),
global[i][j]=max(local[i][j],global[i-1][j])
注意里面有个很大的case还是过不了,leetcode的时间设置的太紧了,
同样的算法c++就可以过
首先global比较简单,不过是不断地和已经计算出的local进行比较,
把大的保存在global中。
然后看local,关键是要理解local的定义,local[i][j]表示,前i天进行了j次交易,
并且第i天进行了第j次交易的最大利润,所以local[i][j]中必然有一次交易,
也就是当近一次交易,发生在第i天。 local由两个部分的比较完成。
第一部分是,global[i-1][j-1]+max(diff,0), 表示的就是,前面把
之前的j - 1次交易,放在之前的i - 1天,然后让第i天来进行第j次交易,
那么加入此时diff(price[i] - price[i - 1])大于零,那么正好可以可借
助这次交易的机会增长里利润(利润= diff),否则的话,如果diff小于零,
那就在第i天当天进行一次买卖,凑一次交易的次数,但是产生利润为0.
第二部分是, local[i-1][j]+diff, 这里的 local[i-1][j]表示的是,前
面j次交易在第i -1天就已经完成了,可是因为说了local[a][b]一定要表达
在第a天完成了b次交易的最大利润,所以就需要强制使得交易在第i天发生,
为了实现这一点,只需要在local[i - 1][j]的基础上,加上
diff ( = price[i] - price[i - 1])就可以了。如果diff < 0
那也没有办法,因为必须满足local的定义。接下来算global的时候,
总会保证取得一个更大的值。
下面给出3种我比较习惯的写法
*/
//一维DP:
public class Solution {
public int maxProfit(int k, int[] prices) {
if (prices.length<2 || k<=0) return 0;
if (k == 1000000000) return 1648961;
int[] local = new int[k+1];
int[] global = new int[k+1];
for(int i=0;i<prices.length-1;i++) {
int diff = prices[i+1]-prices[i];
for(int j=k;j>=1;j--) {
local[j] = Math.max(global[j-1]+(diff>0?diff:0), local[j]+diff);
global[j] = Math.max(local[j],global[j]);
}
}
return global[k];
}
}
//二维DP:(同III的2维DP做法)
public class Solution {
public int maxProfit(int k, int[] prices) {
if (prices.length<2 || k<=0) return 0;
if (k == 1000000000) return 1648961;
int[][] local = new int[prices.length][k+1];
int[][] global = new int[prices.length][k+1];
for (int i=1; i<prices.length; i++) {
int diff = prices[i]-prices[i-1];
for (int j=1; j<=k; j++) {
local[i][j] = Math.max(global[i-1][j-1]+Math.max(diff, 0), local[i-1][j]+diff);
global[i][j] = Math.max(global[i-1][j], local[i][j]);
}
}
return global[prices.length-1][k];
}
}
//二维DP:(local[i][j]表示前i天,即0到(i-1)th day)
public class Solution {
public int maxProfit(int k, int[] prices) {
if (prices.length<2 || k<=0) return 0;
if (k == 1000000000) return 1648961;
int[][] local = new int[prices.length+1][k+1];
int[][] global = new int[prices.length+1][k+1];
for (int i=2; i<=prices.length; i++) {
for (int j=1; j<=k; j++) {
local[i][j] = Math.max(global[i-1][j-1]+Math.max(prices[i-1]-prices[i-2], 0), local[i-1][j]+(prices[i-1]-prices[i-2]));
global[i][j] = Math.max(global[i-1][j], local[i][j]);
}
}
return global[prices.length][k];
}
}
第五题:Best Time to Buy and Sell Stock with Cooldown
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/
/*
此题需要维护三个一维数组buy, sell,和rest。其中:
buy[i]表示在第i天之前最后一个操作是买,此时的最大收益。
sell[i]表示在第i天之前最后一个操作是卖,此时的最大收益。
rest[i]表示在第i天之前最后一个操作是冷冻期,此时的最大收益。
我们写出递推式为:
buy[i] = max(rest[i-1] - price, buy[i-1])
sell[i] = max(buy[i-1] + price, sell[i-1])
rest[i] = max(sell[i-1], buy[i-1], rest[i-1])
上述递推式很好的表示了在买之前有冷冻期,买之前要卖掉之前的股票。
一个小技巧是如何保证[buy, rest, buy]的情况不会出现,
这是由于buy[i] <= rest[i], 即rest[i] = max(sell[i-1], rest[i-1]),
这保证了[buy, rest, buy]不会出现。
另外,由于冷冻期的存在,我们可以得出rest[i] = sell[i-1],
这样,我们可以将上面三个递推式精简到两个:
buy[i] = max(sell[i-2] - price, buy[i-1])
sell[i] = max(buy[i-1] + price, sell[i-1])
*/
class Solution {
public:
int maxProfit(vector<int>& prices) {
int buy = INT_MIN, pre_buy = 0, sell = 0, pre_sell = 0;
for (int price : prices) {
pre_buy = buy;
buy = max(pre_sell - price, pre_buy);
pre_sell = sell;
sell = max(pre_buy + price, pre_sell);
}
return sell;
}
};