要求:
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 k=1,即对交易的次数有要求,只能买卖一只股票一次,求出所能s获取的最大利润。 注意:不能在买入股票之前卖出股票。
参考链接:
labuladong.gitbook.io/algo/di-lin…
示例:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
思路:
每天有三种选择:买入、卖出、无操作。有三种状态:天数、允许交易的最大次数、当前的持有状态(1表示持有,0表示没有持有)。dp[3][2][1]代表今天是第三天,最多进行两笔交易,持有股票。dp[2][3][0]代表第二天,最多进行三次交易,手上不持有股票。最终想求的答案是dp[n-1][k][0]。
状态转移方程:
dp[i][k][0]=max(dp[i-1][k][0],dp[i-1][k][1]+prices[i])=max(选择rest, 选择sell)
dp[i][k][1]=max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i])=max(选择rest, 选择buy)
初始特殊情况的说明:
base case:
dp[-1][k][0]=dp[i][0][0]=0(i=-1说明还没有开始,利润为0;k=0意味着不允许交易,利润为0)
dp[-1][k][1]=dp[i][0][1]=-infinity(i=-1说明还没开始,却持有股票,根本不存在这样的情况,所以用负无穷表示;k=0表示不允许交易,也就不可能持有股票,用负无穷表示)
状态转移方程:
dp[i][k][0]=max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1]=max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
对于k=1的情况,可以简化base case 和状态方程
dp[i][1][0]=max(dp[i-1][1][0], dp[i-1][1][1] + prices[i])
dp[i][1][1]=max(dp[i-1][1][1], d[i-1][0][0]-prices[i])=max(dp[i-1][1][1], -prices[i])
又因为k都是1,不会改变,k对状态转移方程没有影响,可以简化再去掉k
dp[i][0]=max(dp[i-1][0], dp[i-1][1]+prices[i])
dp[i][1]=max(dp[i-1][1], -prices[i])
当i=0时的特殊情况:
dp[0][0] = 0;
//dp[0][0]=max(dp[-1][0], dp[-1][1] + prices[i])=0
dp[0][1] = -prices[i];
//
注意到,在状态转移方程中,新状态只和相邻的一个状态有关,所以不用新建整个dp数组,只需要用一个变量存储相邻的状态就可以了。
代码:
class Solution {
public int maxProfit(int[] prices) {
int dp_i_0 = 0;
int dp_i_1 = -prices[0];
int len = prices.length;
for (int i = 0; i < len; i++) {
dp_i_0 = Math.max(dp_i_0, dp_i_1+prices[i]);
dp_i_1 = Math.max(dp_i_1, -prices[i]);
}
return dp_i_0;
}
}