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).
Example 1:
Input: [2,4,1], k = 2
Output: 2
Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2.
Example 2:
Input: [3,2,6,5,0,3], k = 2
Output: 7
Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4.
Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
-----------------------------------------------------------------------2018.11.18更新-------------------------------------------------------------------------
这题思路应该这么来的,sell[k][p] k表式第几次卖,p表式第几天。可以分成当天卖(sell[k][p-1])与不买两个事件,又可以把当天卖的事件再分成 之前第几天买(如果分成上一次第j天卖,则下面无法优化时间复杂度)。则得到下面O(n^3)代码。
sell[k][p]=max(sell[k][p-1],max(sell[k-1][j-1]-prices[j]+prices[p]))
class Solution {
public int maxProfit(int k, int[] prices) {
// int[] buy=new int[k];
if(k==0||prices.length==0) return 0;
if(k>prices.length/2){
int sum=0;
for(int i=1;i<prices.length;i++){
if(prices[i]>prices[i-1]){
sum+=prices[i]-prices[i-1];
}
}
return sum;
}
int[][] sell=new int[k+1][prices.length];
//sell[p][k]=max(p,k)+max(buy[i])
for(int i=1;i<k+1;i++){
for(int j=1;j<prices.length;j++){
sell[i][j]=sell[i][j-1];
for(int m=j-1;m>=0;m--){
sell[i][j]=Math.max(sell[i][j],(m==0?0:sell[i-1][m-1])-prices[m]+prices[j]);//卖和买同一天情况肯定不是
}
}
}
return sell[k][prices.length-1];
}
}
注意求sell[i][j]的时候存在大量重复每次j循环中sell[i-1][m-1]-prices[m] 重复求了,所以每次j循环中维护一个sell[i-1][m-1]-prices[m] 变量,能减少时间复杂度。
class Solution {
public int maxProfit(int k, int[] prices) {
if(k==0||prices.length==0) return 0;
if(k>prices.length/2){
int sum=0;
for(int i=1;i<prices.length;i++){
if(prices[i]>prices[i-1]){
sum+=prices[i]-prices[i-1];
}
}
return sum;
}
int[][] sell=new int[k+1][prices.length];
for(int i=1;i<k+1;i++){
int temp=sell[i-1][0]-prices[0];
for(int j=1;j<prices.length;j++){
sell[i][j]=Math.max(sell[i][j-1],temp+prices[j]);
temp=Math.max(temp,sell[i-1][j-1]-prices[j]);
}
}
return sell[k][prices.length-1];
}
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
这题比121. Best Time to Buy and Sell Stock ,122. Best Time to Buy and Sell Stock II 难了不知道多少倍。
只能看讨论区大神的解答了。知道了可以用二维dp做。dp[i][j]中i代表交易次数,j代表了到那一天,dp[i][j]代表了此时的最大利润。关键是递推式怎么得到???
可以分两种情况:
1.当最后一天没有卖出股票时。dp[i][j]=dp[i][j-1]。
2.当最后一天卖出股票时。dp[i][j]=max(dp[i-1][jpre]+prices[j]-prices[jpre]),jpre是之前某一天的价格,这里的意思是,jpre天之前交易了i-1次,最后一次交易为prices[j]-prices[jpre]。这样每次还要计算最大的jpre值??把上式改造一下为dp[i][j]=prices[j]+max(dp[i-1][jpre]-prices[jpre])。这样有一个好处,遍历j时能维护一个最大的dp[i-1][jpre]-prices[jpre],就不用每次都计算了。
所以总的递推式为dp[i][j] = max(dp[i][j-1] , prices[j]+max(dp[i-1][jpre]-prices[jpre]))。
初始条件:dp[0][j]=0;dp[i][0]=0。
这里还要考虑一个内存溢出的问题,当k值过大是k>prices.length/2时,问题转化为可以leetcode 122. Best Time to Buy and Sell Stock II 。可以进行任意多次交易。
public int maxProfit(int k, int[] prices) {
if(prices==null||prices.length==0) return 0;
if(k>prices.length/2) return maxProfit2(prices);//转换为问题2的情况
int[][] dp=new int[k+1][prices.length];
for(int i=1;i<k+1;i++){
int maxtemp=dp[i-1][0]-prices[0];
for(int j=1;j<prices.length;j++){//在变动的j时维护一个max(dp[k-1][j]-prices[j])
dp[i][j]=Math.max(dp[i][j-1],prices[j]+maxtemp);
maxtemp=Math.max(maxtemp,dp[i-1][j]-prices[j]);
}
}
return dp[k][prices.length-1];
}
public int maxProfit2(int[] prices){
int sum=0;
for(int i=1;i<prices.length;i++){
if(prices[i]>prices[i-1]){
sum+=prices[i]-prices[i-1];
}
}
return sum;
}
这题没想到解法,主要是没想到递推式。递推式要分析各种情况,但是如何找到各种情况?有时可以考虑当前问题的最后一步的各种情况。从最后一步入手,这里就是最后一天的股票的处理,通过分析两种情况和子问题建立了联系。这和编辑距离那一题有点像,那题是考虑最后一个字符的各种处理情况入手的。
这题的还有一个难点是求解递推式时的一个技巧,不用再一次遍历求max(dp[i-1][jpre]-prices[jpre]),在遍历时自动更新就行了,这也是减少了重复计算。减少重复计算往往也是减少时间复杂度的本质。需要用心体会。