一.力扣123.买卖股票的最佳时机Ⅲ
题目描述
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
示例 2:
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。
示例 4:
输入:prices = [1]
输出:0
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 105
方法一:无脑拆分为五种状态,然后用dp
每一天股票细分为五种状态:
-
未持股,且未卖过,f0
-
未持股,卖过一次,f1
-
持股,第一次持股,f2
-
持股,第二次持股,f3
-
未持股,卖过两次,f4
f 表示在该种状态下当日过后的累计最大金额,有状态方程如下:
f [ i ] [ 0 ] = f [ i − 1 ] [ 0 ] ; f[i][0]=f[i-1][0]; f[i][0]=f[i−1][0];
f [ i ] [ 1 ] = m a x ( f [ i − 1 ] [ 2 ] + p r i c e s [ i ] , f [ i − 1 ] [ 1 ] ) ; f[i][1]=max(f[i-1][2]+prices[i],f[i-1][1]); f[i][1]=max(f[i−1][2]+prices[i],f[i−1][1]);
f [ i ] [ 2 ] = m a x ( f [ i − 1 ] [ 2 ] , − p r i c e s [ i ] ) ; f[i][2]=max(f[i-1][2],-prices[i]); f[i][2]=max(f[i−1][2],−prices[i]);
f [ i ] [ 3 ] = m a x ( f [ i − 1 ] [ 3 ] , f [ i − 1 ] [ 1 ] − p r i c e s [ i ] ) ; f[i][3]=max(f[i-1][3],f[i-1][1]-prices[i]); f[i][3]=max(f[i−1][3],f[i−1][1]−prices[i]);
f [ i ] [ 4 ] = m a x ( f [ i − 1 ] [ 4 ] , f [ i − 1 ] [ 3 ] + p r i c e s [ i ] ) ; f[i][4]=max(f[i-1][4],f[i-1][3]+prices[i]); f[i][4]=max(f[i−1][4],f[i−1][3]+prices[i]);
代码实现:
int maxProfit(int* prices, int pricesSize){
//f 0:未持股,且未卖过
//f 1:未持股,卖过一次
//f 2:持股,第一次持股
//f 3:持股,第二次持股
//f 4: 未持股,卖过两次
int f[pricesSize][5];
f[0][0]=f[0][1]=f[0][4]=0;
f[0][2]=f[0][3]=-prices[0];
int i;
for(i=1;i<pricesSize;i++)
{
f[i][0]=f[i-1][0];
f[i][1]=fmax(f[i-1][2]+prices[i],f[i-1][1]);
f[i][2]=fmax(f[i-1][2],-prices[i]);
f[i][3]=fmax(f[i-1][3],f[i-1][1]-prices[i]);
f[i][4]=fmax(f[i-1][4],f[i-1][3]+prices[i]);
}
return fmax(fmax(f[pricesSize-1][0],f[pricesSize-1][1]),f[pricesSize-1][4]);
}
二.力扣123.买卖股票的最佳时机Ⅳ
题目描述
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
示例 2:
输入:k = 2, prices = [3,2,6,5,0,3]
输出:7
解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。
提示:
0 <= k <= 100
0 <= prices.length <= 1000
0 <= prices[i] <= 1000
方法一:划分2k种状态,用dp
上一题最多交易2次,我们划分了五种状态,而本题的状态数是根据输入值k变化的
我们把状态分为两个大类,分别为:
- 持股状态posession
- 未持股状态empty
其中每一个大类中又能分为k种小状态,分别为第i次持股,未持股且卖出了i次
用posession[i][j]和empty[i][j]表示第i天j种状态下,当天过完之后的最大累计收益
在此状态划分下稍加思索得出状态方程如下:
p
o
s
e
s
s
i
o
n
[
i
]
[
j
]
=
m
a
x
(
p
o
s
e
s
s
i
o
n
[
i
−
1
]
[
j
]
,
e
m
p
t
y
[
i
−
1
]
[
j
−
1
]
−
p
r
i
c
e
s
[
i
]
)
;
posession[i][j]=max(posession[i-1][j],empty[i-1][j-1]-prices[i]);
posession[i][j]=max(posession[i−1][j],empty[i−1][j−1]−prices[i]);
e
m
p
t
y
[
i
]
[
j
]
=
m
a
x
(
e
m
p
t
y
[
i
−
1
]
[
j
]
,
p
o
s
e
s
s
i
o
n
[
i
−
1
]
[
j
]
+
p
r
i
c
e
s
[
i
]
)
;
empty[i][j]=max(empty[i-1][j],posession[i-1][j]+prices[i]);
empty[i][j]=max(empty[i−1][j],posession[i−1][j]+prices[i]);
此外,还需对边界条件和初始值稍加处理,即:
- 第0天未持股的状态,初始值一定是0
- 第0天持股的状态,初始值一定是负的第0天的股票价格:-prices[i]
- 第i天持股状态下,第0次持股的值其实等价于未持股,也将初始值设置为0
- 第i天未持股状态下且0次卖出的值一定是0,设置初始值为0
最终返回的结果应当是最后一天未持股状态中累计金额最大的结果,可以遍历比较获得
代码实现:
int maxProfit(int k, int* prices, int pricesSize){
int posession[pricesSize][k+1],empty[pricesSize][k+1];
int i,j,max=-999;
for(j=0;j<k+1;j++){empty[0][j]=0;posession[0][j]=-prices[0];}
for(i=1;i<pricesSize;i++){empty[i][0]=0;posession[i][0]=0;}
for(i=1;i<pricesSize;i++)
{
for(j=1;j<k+1;j++)
{
//第j次持股
posession[i][j]=fmax(posession[i-1][j],empty[i-1][j-1]-prices[i]);
//未持股,且卖了j次
empty[i][j]=fmax(empty[i-1][j],posession[i-1][j]+prices[i]);
}
}
for(i=0;i<k+1;i++)//获取最大值返回
{
if(max<empty[pricesSize-1][i]) max=empty[pricesSize-1][i];
}
return max;
}