动态规划问题主要就是要明确dp函数定义、搞清楚状态以及状态转移方程
188. 买卖股票的最佳时机 IV - 力扣(LeetCode)
对于股票,我们每天有三种选择 => buy, sell, hold
限制条件有 => 天数限制(n),买入股票次数限制(k)
状态(动态过程) => [第 i 天][还剩下 j 次买入机会][有无股票]
dp[i][k][0 or 1] 表示收益
0 <= i < n, 1 <= k <= K
n 为天数,大 K 为交易数的上限,0 和 1 代表是否持有股票。
此问题共 n × K × 2 种状态,全部穷举就能搞定。for 0 <= i < n:
for 1 <= k <= K:
for s in {0, 1}:
dp[i][k][s] = max(buy, sell, rest)
最终所求状态 => dp[ n ][ k ][ 0 ] (最后不把股票抛出去不就是全亏嘛)
根据每天的三种选择(buy, sell, hold)
=> 两种状态转移:
1. dp[ i ][ k ][0](不持有股票) = max(hold, sell) = max(dp[i - 1][k][0], dp[i - 1][k - 1][1] + price[i] );
2. dp[ i ][ k ][1] (持有股票) = max(hold, buy) = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - price[i]);
1. 从天数角度 => 下标从0开始
dp[0][...][0] = 0;
dp[0][...][1] = -prices[0];(可以赋值INT_MIN方便max())
2. 从交易次数角度 => k = 0表示不允许交易,当然不可能持有股票 => NAN
dp[...][0][0] = 0;
dp[...][0][1] = NAN;
121. 买卖股票的最佳时机 - 力扣(LeetCode)
仅有一次买入机会
class Solution {
public:
int maxProfit(vector& prices) {
int n = prices.size();
vector> dp(n, vector(2));
dp[0][0] = 0, dp[0][1] = -prices[0];
for(int i = 1; i < n; i++) {
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);
dp[i][1] = max(dp[i-1][1], -prices[i]);
}
return dp[n-1][0];
}
};
马尔可夫链 => 空间压缩
class Solution {
public:
int maxProfit(vector& prices) {
int n = prices.size();
int dp_0 = 0, dp_1 = -prices[0];
for(int i = 1; i < n; i++) {
dp_0 = max(dp_0, dp_1 + prices[i]);
dp_1 = max(dp_1, -prices[i]);
}
return dp_0;
}
};
122. 买卖股票的最佳时机 II - 力扣(LeetCode)
无限次买入机会
class Solution {
public:
int maxProfit(vector& prices) {
int n = prices.size();
vector> dp(n, vector(2));
dp[0][0] = 0, dp[0][1] = -prices[0];
for(int i = 1; i < n; i++) {
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);
}
return dp[n-1][0];
}
};
空间压缩
class Solution {
public:
int maxProfit(vector& prices) {
int n = prices.size();
int dp_0 = 0, dp_1 = -prices[0];
for(int i = 1; i < n; i++) {
int temp = dp_0;
dp_0 = max(dp_0, dp_1 + prices[i]);
dp_1 = max(dp_1, temp - prices[i]);
}
return dp_0;
}
};
123. 买卖股票的最佳时机 III - 力扣(LeetCode)
k = 2;