一.问题背景
1.在给定的n天股票价格中,在最多进行k次交易下的最大利润。
2.每次交易为买入和卖出一个周期(不允许在卖出前买入)。
算法目的: 若给定股票每日股价序列price={n₁,n₂,n₃···n_(m)},要求在指定交易次数下k下,求得最大利润。
二.动态规划
问题结构分析
(1)给出问题表示
profit[i][j]:表示进行 i 次交易在第 j 天所能获得的最大利润,在每次卖出后更新。
(2)明确原始问题
profit[k][n-1]:在k次交易下利润的最大值。
递归关系建立
交易行为分为买入,不卖出和卖出:
买入的情况:
当确定卖出后能够实现更大利润的时候才确定是否购买。
不卖出的情况:
不交易利润与前一天相同,便有:profit[i][j]=profit[i][j-1]。
买入卖出的情况:
如果在第 n天卖出股票,则需要找出之前某一天 j (0<=j<n)买入
股票,此笔交易的利润为price[n]-price[j-1]。
再加上在第 j 天之前进行的第i-1次交易所获得的最大利润,即
profit[i-1][j-1]。为保证利润最大需使profit[i - 1][j - 1] - price[j-1]最大
因此第 n 天卖出获得的最大利润为price[n]+profit[i-1][j-1]-price[j-1],可写为price[n]+max(profit[i-1][j-1]-price[j])(0<=j<n)。
综合三种情况:
profit[i][n]=max(profit[i][n-1],price[n]+max(profit[i-1][j-1]-price[j-1]))
为了方便计算,我们引入了prevDiff来存储 profit[i-1][j-1]-price[j-1] 的最大值,即prevDiff=max(prevDiff, profit[i−1][j−1]−price[j-1])。
经过替换为 i,j 则有: profit[i][j]=max(profit[i][j−1],price[j]+prevDiff)。
状态表示
(1)profit[i][j]为原问题的子问题。
(2)子问题通过i(第几次交易)和j(第几天)两个参数来确定。
(3)profit[i][j]表示最多进行i次交易在第j天的最大利润。
(4)profit[k][n-1]为原问题的最大利润。
状态递归方程
(1)当i=0或j=0时,profit[i][j]=0
(2)当i,j>0时,profit[i][j]分为两种情况:
①当不交易时,profit[i][j]=profit[i][j-1]))。
②当发生交易时,profit[i][j]=max(profit[i][j-1],profit[j]+prevDiff)(prevDiff=max(prevDiff,profit[i-1][j-1]))。
自底向上计算
初始化
profit[i][0]=profit[0][j]=0 (前者代表天数为0如果只有一天,不管允许多少次交易,利润都是 0,因为没有足够的时间进行买卖;后者表示如果没有交易,即使有 i 天,利润也是 0 )。
递推公式
prevDiff=max(prevDiff,profit[i−1][j−1]−price[j−1] )
profit[i][j] = max(profit[i][j−1],price[j]+prevDiff)
填表
price={1,3,7,4,8,2,7}
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 2 | 6 | 6 | 7 | 7 | 7 |
2 | 0 | 2 | 6 | 6 | 10 | 10 | 12 |
3 | 0 | 2 | 6 | 6 | 10 | 10 | 15 |
- 这里以填profit[1][1]为例:
prevDiff= max(prevDiff,profit[0][0]−price[0] )=max(-∞,-1)= -1
profit[1][1]=max(profit[1][0],price[1]+prevDiff)=max(0,2)=2
- 这里以填profit[1][2]为例:
prevDiff= max(prevDiff,profit[0][1]−price[1] )=max(-1,-3)= -1
profit[1][2]=max(profit[1][1],price[2]+prevDiff)=max(2,6)=6
- 这里以填profit[2][1]为例:
prevDiff= max(prevDiff,profit[1][0]−price[0] )=max(-∞,-1)= -1
profit[2][1]=max(profit[2][0],price[1]+prevDiff)=max(0,2)=2
- 当填到profit[k][n-1]得到最大利润
最优追踪方案
- 找到最后卖出点
if (profit[v][b] != profit[v][b - 1]) 说明在第b天卖出了股票,反之则进行b-- 操作,知道找到最后卖出点(此时v=k,b=n-1),从后往前追溯。
- 找到相应的买入点
if (profit[v - 1][i] - price[i] == profit[v][b] - price[b])由总利润-售出价格=买入后,卖出前的利润。应等于上次利润-买入成本。若不等则i–往前比较。循环操作(1)和操作(2)直到v或b<=0便可完成全追溯过程
- 例如:横坐标为j,纵坐标为i
price={1,3,7,4,8,2,7}
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 2 | 6 | 6 | 7 | 7 | 7 |
2 | 0 | 2 | 6 | 6 | 10</ |