股票抛售的最佳时机
股票的抛售的最佳时机是动态规划问题中的一类题目,今天笔者就记录一下自己对于这类问题的看法与见解
我们通过动态规划五部曲来解决这道问题
-
确定dp数组的含义与其下标的含义
-
我们分析题目意思可以知道因为股票存在两种状况一种是持有,另一种是不持有。所以我们定义一个二维数组来分别表示不同的状况。
dp[i ][1]表示第i天持有股票所得最多现金
dp[i][0]表示第i天不持有股票所得最多现金
-
-
确定递推公式
-
我们发现每一个可以每一个位置可以从两个方向推出来。
第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0] : dp[i][0] = fmax(dp[i-1][0], dp[i-1][1] + prices[i]);
第i天买入股票,所得现金就是买入今天的股票后所得现金即:dp[i][1] = fmax(-prices[i], dp[i-1][1])
-
-
dp数组初始化
- 我们应该初始化dp数组的dp[0][0]和dp[0][1]。一个初始化为0,另一个初始化为-price[0]。
-
遍历顺序
- 我们应当从前向后进行一个遍历,这样就可以实现一个 遍历
-
推导dp数组
int maxProfit(int* prices, int pricesSize) {
int dp[pricesSize][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for (int i = 1; i < pricesSize; i++) { //进行一个推导
dp[i][0] = fmax(dp[i-1][0], dp[i-1][1] + prices[i]);
dp[i][1] = fmax(-prices[i], dp[i-1][1]);
}
if (dp[pricesSize-1][0] > dp[pricesSize - 1][1]) {
return dp[pricesSize -1][0];
}
else {
return dp[pricesSize -1][1];
}
}
这道题目也可以用贪心算法实现
我们只要找出局部最优就可以实现,我们每次找最低的,然后计算每一次售卖可以得到的利润,从而得到一个最大利润。
int maxProfit(int* prices, int pricesSize) {
int low = prices[0];
int result = 0;
for (int i = 0; i < pricesSize; i++) {
low = fmin(low, prices[i]); //
result = fmax(result, prices[i] - low);
}
return result;
}
之后我们在将问题提升一下难度
122. 买卖股票的最佳时机 II - 力扣(LeetCode)
这次引入了多次售卖,因此我们的dp数组的递推公式需要进行一定的变化,其他和上面的相同。
第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp**[i - 1]**[1]
第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp**[i - 1]**[0]
dp[i][0] = fmax(dp[i - 1][0], dp[i - 1][1] - prices[i]);
dp[i][1] = fmax(dp[i - 1][0] + prices[i], dp[i - 1][1]);
其他就是相同的
int maxProfit(int* prices, int pricesSize) {
int dp[pricesSize][2];
dp[0][0] = -prices[0];
dp[0][1] = 0;
for (int i = 1; i < pricesSize; i++) {
dp[i][0] = fmax(dp[i - 1][0], dp[i - 1][1] - prices[i]);
dp[i][1] = fmax(dp[i - 1][0] + prices[i], dp[i - 1][1]);
}
return fmax(dp[pricesSize -1][0], dp[pricesSize - 1][1]);
}
本题也有贪心解法:将利润拆分成每一天的利润只要是正利润就相加
int maxProfit(int* prices, int pricesSize){
int result = 0;
int i;
for(i = 1; i < pricesSize; ++i) {
if(prices[i] > prices[i-1])
result+= prices[i]-prices[i-1];
}
return result;
}
123. 买卖股票的最佳时机 III - 力扣(LeetCode)
这题目固定了售卖的次数,所以我们需要重新分析这道题目
-
确定dp数组和其下标含义
- 没有操作 (其实我们也可以不设置这个状态)
- 第一次持有股票
- 第一次不持有股票
- 第二次持有股票
- 第二次不持有股票
-
分析递推公式
-
我们发现其实递推公式还是和上一道一样,只不过我们需要考虑的是第几次操作。
dp[i][0] = dp[i - 1][0]; dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]); dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i]); dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]); dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
-
-
初始化
- dp[0][0]和dp[0][2]初始化为0
- dp[1][1]和dp[0][3]初始化为-price[0];
-
遍历顺序
- 遍历顺序 依旧是从前到后。
-
举例递推
代码如下:
int maxProfit(int* prices, int pricesSize) {
int dp[pricesSize][5];
dp[0][0] = 0;
dp[0][2] = 0;
dp[0][1] = -prices[0];
dp[0][3] = -prices[0];
dp[0][4] = 0;
for(int i = 1; i < pricesSize; i++) {
dp[i][0] = dp[i - 1][0];
dp[i][1] = fmax(dp[i - 1][1], dp[i - 1][0] - prices[i]); //和第二题一样枚举情况
dp[i][2] = fmax(dp[i - 1][2], dp[i - 1][1] + prices[i]);
dp[i][3] = fmax(dp[i - 1][3], dp[i - 1][2] - prices[i]);
dp[i][4] = fmax(dp[i - 1][4], dp[i - 1][3] + prices[i]);
}
return dp[pricesSize - 1][4];
}
笔者能力有限,理解浅薄,若有错误还请指出。