思路:动态规划
dp[ i ][ j ][ k ]
i - 到第 i 天
j - 最多交易 j 次
k - 0:当前持股 / 1:当前不持股
状态转移方程
// 第i天,最多交易j次,当前不持股
dp[i][j][0] = MAX{
dp[i-1][j][0], // 本次不持-上次就不持
dp[i-1][j-1][1] + 差价 // 本次不持-本次卖
}
// 第i天,最多交易j次,当前持股
dp[i][j][1] = MAX{
dp[i-1][j][0], // 本次持-本次买
dp[i-1][j][1] + 差价 // 本次持-上次就持
}
public int getdp(int i,int j,int k,int[][][] dp,int[] prices){
if(dp[i][j][k]==-1){
if(i<1){
dp[i][j][0]=0;
dp[i][j][1]=0;
}else{
// 本次不持-上次就不持
int dp1 = getdp(i-1,j,0,dp,prices);
// 本次不持-本次卖
int dp2 = j>0 ? getdp(i-1,j-1,1,dp,prices)+prices[i]-prices[i-1] : 0;
dp[i][j][0] = Math.max(dp1,dp2);
// 本次持-本次买
int dp3 = getdp(i-1,j,0,dp,prices);
// 本次持-上次就持
int dp4 = getdp(i-1,j,1,dp,prices)+prices[i]-prices[i-1];
dp[i][j][1] = Math.max(dp3,dp4);
}
}
return dp[i][j][k];
}
坑:当k很大时,内存会爆,此时相当于不限制交易次数,直接用贪心解决
class Solution {
public int maxProfit(int k, int[] prices) {
if(prices.length<2){
return 0;
}
if(k > prices.length/2){
// 相当于不限制次数,贪心
int value = 0;
for(int i=1;i<prices.length;i++){
value += prices[i]>prices[i-1] ? prices[i]-prices[i-1] : 0;
}
return value;
}
int[][][] dp = new int[prices.length][k+1][2];
for(int i=0;i<prices.length;i++){
for(int j=0;j<k+1;j++){
Arrays.fill(dp[i][j],-1);
}
}
return getdp(prices.length-1,k,0,dp,prices);
}
public int getdp(int i,int j,int k,int[][][] dp,int[] prices){...}
}
用这一套解法干掉4题
Leetcode 121 买卖股票的最佳时机 —— 最多交易1次
令 k = 1
Leetcode 122 买卖股票的最佳时机 II —— 不限次数
直接贪心
Leetcode 123 买卖股票的最佳时机 III —— 最多交易2次
令 k = 2