一、买卖股票的最佳时机含手续费
题目描述:
题目分析:
1、状态分析
其实一共就只有两种状态,不是处于 持有 状态,就是处于 未持有 状态。
- f[i]表示第i天结束后,处于 持有 状态时,最大的利润
- g[i]表示第i天结束后,处于 未持有 状态时,最大的利润
2、状态转移方程
对于f[i],有两种情况可以到达这种状态:
- 在第 i-1 天时就已处于 持有 状态,然后第 i 天啥也不干,仍然保持 持有 状态。此时的最大利润就为 f[i - 1]
-
在 i - 1 天的时候「未持有」股票,在第 i 天买⼊股票。此时最⼤收益为 g[i - 1] - prices[i])
f[i] = max(f[i - 1], g[i - 1] - prices[i])
对于g[i],也有两种情况能够到达这种状态:
-
在 i - 1 天「持有」股票,但是在第 i 天将股票卖出。此时最⼤收益为: f[i - 1]+ prices[i] - fee) ,记得⼿续费
-
在 i - 1 天「未持有」股票,然后第 i 天啥也不⼲。此时最⼤收益为: g[i - 1] ;
可知 g[i] 就为以上两种情况下的最大值,即
g[i] = max(g[i - 1], f[i - 1] + prices[i] - fee) 。
3、初始化
为了避免数组访问越界,我们需要初始化数组
- 对于 f[0] ,此时处于「持有」状态,因此 f[0] = -prices[0]
- 对于 g[0] ,此时处于「未持有」状态,此时的最大收益就是0,因此 g[0] = 0
代码实现:
class Solution {
public int maxProfit(int[] prices, int fee) {
//int n=prices.length;
// f 卖出状态 g 买入状态
int f=0,g=-0x3f3f3f;
for(int price:prices){
int new_f=Math.max(f,g+price-fee);
g=Math.max(g,f-price);
f=new_f;
}
return f;
}
}
二、买卖股票的最佳时机含冷冻期
题目描述:
题目分析:
1、状态表示
这时就会存在 “持有”、“可交易”、“冷冻期” 这三种状态:
- f[i]表示第i天结束后,处于 可交易 状态时,最大的利润
- g[i]表示第i天结束后,处于 持有 状态时,最大的利润
- h[i]表示第i天结束后,处于 冷冻期 状态时,最大的利润
2、状态转移方程
- 在 i - 1 天就处于 可交易 状态,然后第 i 天啥也不做,此时最⼤收益应该和 i - 1 天的保持⼀致: f[i - 1]
- 在 i-1 天就处于 冷冻期 状态,然后第 i 天啥也不做,此时最⼤收益应该和 i - 1 天的保持⼀致: h[i - 1]
- 在 i - 1 天持有股票,然后第 i 天啥也不做,此时最⼤收益应该和 i - 1 天的保持⼀致: g[i - 1]
- 在 i 天买⼊股票,那我们应该选择 i - 1 天不在冷冻期的时候买⼊,即 可交易状态,由于买⼊需要花钱,所以此时最⼤收益为: h[i] - prices[i]
- 在 i - 1 天持有股票,并且卖出股票
此时状态转移方程就为 h[i]=g[i-1]+prices[i]
3、初始化
为了避免数组访问越界,需要对数组进行初始化
- 对于 f[0] ,此时没有进行任何交易,因此 f[0] = 0
- 对于 g[0] ,此时处于「买入」状态,就必须买下第一天的股票,因此 g[0]=-prices[0]
- 对于 h[0],此时也是由于没有交易,因此 h[0]=0
代码实现:
class Solution {
public int maxProfit(int[] prices) {
int n=prices.length;
//f表示可交易 g表示买入 h表示冷冻期
int[] f=new int[n+1],g=new int[n+1],h=new int[n+1];
g[0]=-prices[0];
for(int i=1;i<n;i++){
f[i]=Math.max(f[i-1],h[i-1]);
g[i]=Math.max(f[i-1]-prices[i],g[i-1]);
h[i]=g[i-1]+prices[i];
}
return Math.max(f[n-1],h[n-1]);
}
}
三、买卖股票的最佳时机 IV
题目描述:
题目分析:
1、状态表示
同样的,其实只存在 持有 与 未持有 两种状态。不过对交易次数有所限定。我们就需要用一个变量来记录交易次数:
- f[ i ][ j ] 表示到第 i 天结束时,至多完成 j 笔交易,持有股票时的最大利润
- g[ i ][ j ] 表示到第 i 天结束时,至多完成 j 笔交易,未持有股票时的最大利润
2、状态转移方程
正常的一次交易应该是买入加上卖出,但为了方便统计,我们选择在买入一次股票时就记录为一次交易。
对于f[ i ][ j ],有两种情况可以能够到达这个状态:
- 第 i-1 天时未持有股票且已进行了 j-1 笔交易,并选择在这一天买入股票。此时的利润就为 g[ i-1 ][ j-1 ]-prices[ i ]
- 第 i-1 天已持有股票且已进行了 j 笔交易,然后第 i 天选择啥也不做。此时的利润就为 f[ i-1 ][ j ]
两种情况应取最⼤值,因此: f[ i ][ j ]=max(g[ i-1 ][ j-1 ]-prices[ i ], f[ i-1 ][ j ])
对于g[ i ][ j ],有两种情况可以能够到达这个状态:
- 第 i-1 天时持有股票且已进行了 j 笔交易,并选择在这一天卖出股票。此时的利润就为 f[ i-1 ][ j ]+prices[ i ]
- 第 i-1 天就未持有股票且已进行了 j 笔交易,然后第 i 天选择啥也不做。此时的利润就为 g[ i-1 ][ j ]
两种情况应取最⼤值,因此: g[ i ][ j ]=max(f[ i-1 ][ j ]+prices[ i ], g[ i-1 ][ j ])
3、优化
由上述状态转移方程我们可以看出,在计算第 i 的状态时,我们只需要知道第 i-1 天的状态就行了。因此我们没必要单独列出一个表记录每一天的状态,可以将状态表示为下面这样:
- f[ i ] 表示到当前天结束时,至多完成 i 笔交易,持有股票时的最大利润
- g[ i ] 表示到当前天结束时,至多完成 i 笔交易,未持有股票时的最大利润
则状态转移方程就为:
- f[ i ]=max(f[ i ],g[ i-1 ]+prices[ i ])
- g[ i ]=max(g[ i ],f[ i ]-prices[ i ])
4、初始化
由于在计算g[ i ]时,会出现 对g[ i ]和f[ i ]-prices[ i ]的比较,而 f[ i ]-prices[ i ] 的值很可能是负数,因此我们需要将g[ i ]初始化为一个比较小的数字,才不会影响计算结果。
在这里我们可以先用一遍遍历将g[ i ]初始化为-0x3f3f3f这样一个比较小的数字。
代码实现:
class Solution {
public int maxProfit(int k, int[] prices) {
int[] f=new int[k+2],g=new int[k+2];
for(int i=0;i<k+2;i++){
g[i]=-0x3f3f3f;
}
for(int p:prices){
for(int i=k+1;i>0;i--){
f[i]=Math.max(f[i],g[i-1]+p);
g[i]=Math.max(g[i],f[i]-p);
}
}
return f[k+1];
}
}
四、买卖股票的最佳时机 III
题目描述:
题目分析:
有了上题的基础,这题就变得非常简单了。只需要将上题的k次遍历改为两次遍历即可
代码实现:
class Solution {
public int maxProfit(int[] prices) {
int k=2;
int[] f=new int[k+2],g=new int[k+2];
for(int i=0;i<k+2;i++){
g[i]=-0x3f3f3f;
}
for(int p:prices){
for(int i=k+1;i>0;i--){
f[i]=Math.max(f[i],g[i-1]+p);
g[i]=Math.max(g[i],f[i]-p);
}
}
return f[k+1];
}
}
那么本篇文章就到此为止了,如果觉得这篇文章对你有帮助的话,可以点一下关注和点赞来支持作者哦。如果有什么讲的不对的地方欢迎在评论区指出,希望能够和你们一起进步✊