分析:
令 f i , j f_{i,j} fi,j表示第 i i i天后拥有 j j j张股票时最多能赚的钱数
显然,对于所有 f i , j f_{i,j} fi,j,转移时共有三种情况
1、不做交易
直接由上一天转移:
f i , j = f i , j f_{i,j} = f_{i,j} fi,j=fi,j
2、买入
由于每 w w w天才能交易一次,所以应从第 i − w − 1 i - w - 1 i−w−1天及以前更新,又第 i − w − 1 i - w - 1 i−w−1天前的信息都会更新到第 i − w − 1 i - w - 1 i−w−1天上,所以转移时只需考虑第 i − w − 1 i - w - 1 i−w−1天。然后再枚举交易前拥有股票数 k k k,因为第 i i i天最多购买 a p i ap_i api股,所以 k k k的枚举范围是 [ m i n ( j − a s i , 0 ) , j ) [min(j - as_i,0),j) [min(j−asi,0),j),所以转移方程为:
f i , j = max k = m a x ( j − a s i , 0 ) j − 1 { f i − w − 1 , k − ( j − k ) a p i } f_{i,j} = \max_{k = max(j - as_i,0)}^{j - 1}\{f_{i - w - 1,k} - (j - k)ap_i\} fi,j=k=max(j−asi,0)maxj−1{fi−w−1,k−(j−k)api}
考虑优化这个柿子,注意到
f i − w − 1 , k − ( j − k ) a p i = f i − w − 1 , k + k ⋅ a p i − j ⋅ a p i f_{i - w - 1,k} - (j - k)ap_i = f_{i - w - 1,k} + k \cdot ap_i - j\cdot ap_i fi−w−1,k−(j−k)api=fi−w−1,k+k⋅api−j⋅api
中 j ∗ a p i j * ap_i j∗api为定值,可以提出到大括号外,所以原柿化为:
f i , j = max k = m a x ( j − a s i , 0 ) j − 1 { f i − w − 1 , k + k ⋅ a p i } − j ⋅ a p i f_{i,j} = \max_{k = max(j - as_i,0)}^{j - 1}\{f_{i - w - 1,k} + k\cdot ap_i\} - j\cdot ap_i fi,j=k=max(j−asi,0)maxj−1{fi−w−1,k+k⋅api}−j⋅api
观察大括号中的柿子,显然可以单调队列优化
3、卖出
类似于情况2,有:
f i , j = max k = j + 1 m i n ( j + b s i , m ) { f i − w − 1 , k + ( k − j ) b p i } f_{i,j} = \max_{k = j + 1}^{min(j + bs_i,m)}\{f_{i - w - 1,k} + (k - j)bp_i\} fi,j=k=j+1maxmin(j+bsi,m){fi−w−1,k+(k−j)bpi}
即
f i , j = max k = j + 1 m i n ( j + b s i , m ) { f i − w − 1 , k + k ⋅ b p i } − j ⋅ b p i f_{i,j} = \max_{k = j + 1}^{min(j + bs_i,m)}\{f_{i - w - 1,k} + k \cdot bp_i\} - j \cdot bp_i fi,j=k=j+1maxmin(j+bsi,m){fi−w−1,k+k⋅bpi}−j⋅bpi
显然也可以单调队列优化
取三种情况的最大值即可
Code:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 2050,inf = 1e9;
int n,m,w,as,bs,ap,bp,l,r,from,ans,f[maxn][maxn],q[maxn];
int read(){
int x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + (c ^ 48),c = getchar();
return x;
}
int main(){
n = read(),m = read(),w = read();
for(int i = 0; i <= n; i ++)
for(int j = 0; j <= m; j ++)
f[i][j] = -inf;
f[0][0] = 0;
for(int i = 1; i <= n; i ++){
ap = read(),bp = read(),as = read(),bs = read();
//不做交易
for(int j = 0; j <= m; j ++) f[i][j] = max(f[i][j],f[i - 1][j]);
from = max(0,i - w - 1);//i - k - 1 < 0 时,i不足w天,只能从0(还未交易过)转移
//买入
l = 1,r = 0;
for(int j = 0; j <= m; j ++){
while(l <= r && q[l] < j - as) l ++;
while(l <= r && f[from][q[r]] + q[r] * ap < f[from][j] + j * ap) r --;
r ++,q[r] = j,f[i][j] = max(f[i][j],f[from][q[l]] + (q[l] - j) * ap);
//单调队列优化(伪)模板
}
//卖出
l = 1,r = 0;
for(int j = m; j >= 0; j --){//卖出时应倒序枚举
while(l <= r && q[l] > j + bs) l ++;
while(l <= r && f[from][q[r]] + q[r] * bp < f[from][j] + j * bp) r --;
r ++,q[r] = j,f[i][j] = max(f[i][j],f[from][q[l]] + (q[l] - j) * bp);
}
}
cout << f[n][0] << endl;
return 0;
}
本文探讨了在特定交易规则下,如何使用动态规划和单调队列优化股票交易策略,以实现收益最大化。通过分析不同交易场景,如买入、卖出和不交易的情况,详细解释了状态转移方程,并给出了具体代码实现。
294

被折叠的 条评论
为什么被折叠?



