首先定义一个能够识别合法的股票交易操作序列的自动机,然后定义相关股票交易操作序列的收益函数。
此时我们可以借助自动机状态与语句的关系,将股票交易操作序列的收益函数,转换为自动机状态相关的收益函数。
此时我们就可以将原问题转换为一个与自动机状态相关的最优化问题。然后通过分析状态转移函数,建立一个状态之间的最优收益转移方程,通过自底向上的DP方法来求解此方程,就可以得到最终问题的解。
给定一个股票每天的交易价格表prices[0,…,n-1],你可以进行k笔交易,但是交易需要符合下述条件:
1、各笔交易不能交叉,即你必须卖掉手上持有的股票,才能再次买新的股票。
求:如果进行k笔交易,能够获得的最大收益
我们首先定义一个自动机,该自动机能够识别符合题目要求的股票交易操作
M = ( Q , q 0 , A , Σ , δ ) Q = s 0 , s 2 , . . . , s 2 k q 0 = s 0 A = { s 2 k } Σ = { b , r , s } δ = 见 下 文 定 义 \begin{aligned} M & = (Q, q_0, A, \Sigma, \delta) \\ Q & = {s_0, s_2, ..., s_{2k}} \\ q_0 & = s_0 \\ A & = \{s_{2k}\} \\ \Sigma & = \{b, r, s\} \\ \delta & = 见下文定义 \end{aligned} MQq0AΣδ=(Q,q0,A,Σ,δ)=s0,s2,...,s2k=s0={s2k}={b,r,s}=见下文定义
δ ( s i , r ) = s i where i mod 2==1 and i < 2k \delta(s_i, r) = s_i \ \text{where i mod 2==1 and i < 2k} δ(si,r)=si where i mod 2==1 and i < 2k
δ ( s i , s ) = s i + 1 where i mod 2==1 and i < 2k \delta(s_i, s) = s_{i+1} \ \text{where i mod 2==1 and i < 2k} δ(si,s)=si+1 where i mod 2==1 and i < 2k
δ ( s i , r ) = s i where i mod 2==0 and i < 2k \delta(s_i, r) = s_i\ \text{where i mod 2==0 and i < 2k} δ(si,r)=si where i mod 2==0 and i < 2k
δ ( s i , b ) = s i + 1 where i mod 2==0 and i < 2k \delta(s_i, b) = s_{i+1}\ \text{where i mod 2==0 and i < 2k} δ(si,b)=si+1 where i mod 2==0 and i < 2k
δ ( s 2 k , r ) = s 2 k where i == 2k \delta(s_{2k}, r) = s_{2k}\ \text{where i == 2k} δ(s2k,r)=s2k where i == 2k
δ ( s i , α ) = d e a t h S t a t e Otherwise \delta(s_i, \alpha) = deathState\ \text{Otherwise} δ(si,α)=deathState Otherwise
接下来对自动机做一些解释:
自动机的字母表 Σ \Sigma Σ表示的是股票的相关操作,s表示sell,b表示buy,r表示持有rest;
该自动机识别的sentence具有下述特性:
1、该sentence中b字符与s字符的数量一致,且均为k
2、b字符与s字符的在序列上看来是交替出现的
3、b是开头字符
所以我们可以知道,一个sentence表示的是一连串的股票交易操作,而能够被该自动机识别的sentence就是一个符合题目要求的股票操作。
接下来我们定义一些收益函数:
p r o f i t ( a , d a y ) : Σ ∗ Z + → Z profit(a, day) : \Sigma*Z^+ \rightarrow Z profit(a,day):Σ∗Z+→Z
该函数表示在第day天的时候进行 a a a股票操作时候的收益:
p r o f i t ( s , i ) = p r i c e s [ i ] profit(s, i) = prices[i] profit(s,i)=prices[i]
p r o f i t ( b , i ) = − p r i c e s [ i ] profit(b, i) = -prices[i] profit(b,i)=−prices[i]
p r o f i t ( r , i ) = 0 profit(r, i) = 0 profit(r,i)=0
再定义,sentence相关的收益函数:
s e n t e n c e = a 0 a 1 a 2 . . . a m d a y s = d 0 d 1 d 2 . . . d m p r o f i t ( s e n t e n c e , d a y s ) = p r o f i t ( a 0 , d 0 ) + p r o f i t ( a 1 , d 1 ) + . . . + p r o f i t ( a m , d m ) \begin{aligned} sentence & = a_0 a_1 a_2 ...a_m\\ days & = d_0 d_1 d_2 ... d_m \\ profit(sentence, days) & = profit(a_0, d_0) + profit(a_1, d_1) + ... + profit(a_m, d_m) \end{aligned} sentencedaysprofit(sentence,days)=a0a1a2...am=d0d1d2...dm=profit(a0,d0)+profit(a1,d1)+...+profit(am,dm)
s e n t e n c e sentence sentence是一连串的股票交易操作;
p r i c e s prices prices保存的是股票每天的交易价格;
d a y s days days记录的是相对应的股票交易日期序列,其应该是不严格递增的序列;
p r o f i t ( s e n t e n c e , d a y s ) profit(sentence, days) profit(sentence,days)表示按照days的日期依次进行sentence中的股票交易操作,最后的收益。
最后,定义一个基于自动机状态的收益函数:
m a x P r o f i t ( s i , j ) = m a x { p r o f i t ( s e n t e n c e , d a y s ) } where δ ( s e n t e n c e ) = s i and d a y s [ m − 1 ] = j \begin{aligned} maxProfit(s_i, j) = max\{ profit(sentence, days) \} \\ \textbf{where}\ \delta(sentence) = s_i \text{ and } days[m-1] = j \end{aligned} maxProfit(si,j)=max{profit(sentence,days)}where δ(sentence)=si and days[m−1]=j
表示在第j天的时候,处于状态 s i s_i si时候的最优收益。
至此我们可以借助上述定义,重新表达原问题,求:
m a x P r o f i t ( s 2 k , n − 1 ) \begin{aligned} maxProfit(s_{2k}, n-1) \end{aligned} maxProfit(s2k,n−1)
其中有: p r i c e s [ 0 , . . . , n − 1 ] prices[0,...,n-1] prices[0,...,n−1]记录了从第0天到第i天的股票交易价格。
做一些解释:
结合前文 m a x P r o f i t maxProfit maxProfit的定义,我们知道 m a x P r o f i t ( s 2 k , n − 1 ) maxProfit(s_{2k}, n-1) maxProfit(s2k,n−1)表示在第n-1天的时候,经过一系列的股票交易操作,此时处于状态s_{2k}的时候的最优收益。
处于 s 2 k s_{2k} s2k状态,就表示了相关股票交易操作是符合题目要求的。
现在我们的问题就是求解:
m a x P r o f i t ( s 2 k , n − 1 ) \begin{aligned} maxProfit(s_{2k}, n-1) & \\ \end{aligned} maxProfit(s2k,n−1)
结合自动机中的状态转移函数,分析某个状态能够达到的可能路径,从中取收益最大的路径,所以有:
m a x P r o f i t ( s i , n − 1 ) = m a x { m a x P r o f i t ( s i − 1 , n − 1 ) + p r o f i t ( s ∣ b , n − 1 ) , m a x P r o f i t ( s i , n − 2 ) } \begin{aligned} maxProfit(s_i, n-1) = & max \{ \\ & maxProfit(s_{i-1}, n-1)+profit(s|b, n-1), \\ & maxProfit(s_i, n-2) \\ & \} \end{aligned} maxProfit(si,n−1)=max{maxProfit(si−1,n−1)+profit(s∣b,n−1),maxProfit(si,n−2)}
p r o f i t ( s ∣ b , n − 1 ) = { p r o f i t ( s , n − 1 ) if i mod 2 == 0 p r o f i t ( b , n − 1 ) if i mod 2 == 1 \begin{aligned} profit(s|b, n-1) = \begin{cases} profit(s, n-1) & \text{if i mod 2 == 0 } \\ profit(b, n-1) & \text{if i mod 2 == 1 } \end{cases} \end{aligned} profit(s∣b,n−1)={profit(s,n−1)profit(b,n−1)if i mod 2 == 0 if i mod 2 == 1
m a x P r o f i t ( s 2 i + 1 , 0 ) = − p r i c e s [ 0 ] m a x P r o f i t ( s 2 i , 0 ) = 0 m a x P r o f i t ( s 0 , j ) = 0 \begin{aligned} maxProfit(s_{2i+1}, 0) = -prices[0] \\ maxProfit(s_{2i}, 0) = 0 \\ maxProfit(s_0, j) = 0 \end{aligned} maxProfit(s2i+1,0)=−prices[0]maxProfit(s2i,0)=0maxProfit(s0,j)=0
至此,我们可以在上述表达式中看到一个最优子结构,所以我们可以借助自底向上的DP来解决该问题。
//求解maxProfit(s_i, n-1)
bottomUpSolution(prices[0,...,n-1])
maxProfit[s0,...,s_{2k}][0,...,n-1];
for i is odd in [0, 2k]
maxProfit[si][0] = -prices[0];
for i is even in [0, 2k]
maxProfit[si][0] = 0;
for j in [0, n-1]
maxProfit[s0][j] = 0;
for j = 1->n-1
for si = s0->s_{2k}
if(i%2 == 0) //特别注意此处,此处不同状态之间的变化,就是通过状态转移函数得到的
//后面309题增加了冷冻期,导致我们的状态转移函数不同,此处需要修改
//另外714题增加了手续费,所以在profit(s, j)的定义也需要修改。
maxProfit[si][j] = max( maxProfit[s_{i-1}][j] + profit(s, j), maxProfit[si][j-1] );
else
maxProfit[si][j] = max( maxProfit[s_{i-1}][j] + profit(b, j), maxProfit[si][j-1] );
return maxProfit[s_{2k}][n-1];
188题是基础,
121 123题是188题的一种具体形式,即具体规定了有多少笔交易,也就是说具体规定过了自动机的状态数目,而自动机的其他定义则完全相同。
122题是188题的一种极限状态下的形式,自动机的形式有比较大的变化。
309题对状态转移函数提出了一些新的限制,需要修改状态转移函数。
714题则是略微修改了收益函数的计算
(给出几道题目的自动机定义(直接给出图),给出几道题目的伪代码,使用文字进行一些说明)