背景:
清明节假期结束了…
题目传送门:
https://www.luogu.org/problemnew/show/P2569
题意:
假的模拟一个股票交易的过程(作弊)。
可以在某一天购买或卖出股票,单价为
A
P
i
,
B
P
i
AP_i,BP_i
APi,BPi;且限制了购买或卖出的数量
A
S
i
,
B
S
i
AS_i,BS_i
ASi,BSi;且两次交易的时间至少为
w
w
w天。
求最后能赚多少钱。
思路:
显然是
D
P
DP
DP。
设
f
i
,
j
f_{i,j}
fi,j表示第
i
i
i天结束交易后持有
j
j
j股的最大利润。
则考虑
4
4
4种情况:
[
1
]
:
[1]:
[1]:凭空买入股票(原来没买):
则
f
i
,
j
=
−
j
∗
A
P
i
f_{i,j}=-j*AP_i
fi,j=−j∗APi(其中
j
≤
A
S
i
j≤AS_i
j≤ASi)。
[
2
]
:
[2]:
[2]:又买入股票(原来买过):
假设原来持有
k
k
k股。
因为两次交易的时间至少为
w
w
w天,所以必须从
f
i
−
w
−
1
,
k
f_{i-w-1,k}
fi−w−1,k转移过来。
则
f
i
,
j
=
max
(
f
i
,
j
,
f
i
−
w
−
1
,
k
−
(
j
−
k
)
∗
A
P
i
)
f_{i,j}=\max(f_{i,j},f_{i-w-1,k}-(j-k)*AP_i)
fi,j=max(fi,j,fi−w−1,k−(j−k)∗APi)(其中
k
>
0
,
0
<
j
−
k
≤
A
S
i
k>0,0<j-k≤AS_i
k>0,0<j−k≤ASi得到
k
∈
[
j
−
A
S
i
,
j
)
k∈[j-AS_i,j)
k∈[j−ASi,j))。
[
3
]
:
[3]:
[3]:不操作
则
f
i
,
j
=
max
(
f
i
,
j
,
f
i
−
1
,
j
)
f_{i,j}=\max(f_{i,j},f_{i-1,j})
fi,j=max(fi,j,fi−1,j)。
[
4
]
:
[4]:
[4]:卖出股票(原来必须持有)
假设原来持有
k
k
k股。
因为两次交易的时间至少为
w
w
w天,所以必须从
f
i
−
w
−
1
,
k
f_{i-w-1,k}
fi−w−1,k转移过来。
则
f
i
,
j
=
max
(
f
i
,
j
,
f
i
−
w
−
1
,
k
+
(
k
−
j
)
∗
B
P
i
)
f_{i,j}=\max(f_{i,j},f_{i-w-1,k}+(k-j)*BP_i)
fi,j=max(fi,j,fi−w−1,k+(k−j)∗BPi)(其中
k
>
0
,
0
<
k
−
j
≤
B
S
i
k>0,0<k-j≤BS_i
k>0,0<k−j≤BSi得到
k
∈
(
j
,
j
+
B
S
i
]
k∈(j,j+BS_i]
k∈(j,j+BSi])。
此时时间复杂度为
Θ
(
n
3
)
\Theta(n^3)
Θ(n3)。
怎么优化?
发现
[
2
]
[
4
]
[2][4]
[2][4]情况太慢,考虑优化这两个。
先考虑
[
2
]
[2]
[2]情况。
f
i
,
j
=
max
(
f
i
,
j
,
f
i
−
w
−
1
,
k
−
(
j
−
k
)
∗
A
P
i
)
f_{i,j}=\max(f_{i,j},f_{i-w-1,k}-(j-k)*AP_i)
fi,j=max(fi,j,fi−w−1,k−(j−k)∗APi)(其中
k
∈
[
j
−
A
S
i
,
j
)
k∈[j-AS_i,j)
k∈[j−ASi,j))
拆括号,得到:
f
i
,
j
=
max
(
f
i
,
j
,
f
i
−
w
−
1
,
k
−
j
∗
A
P
i
+
k
∗
A
P
i
)
f_{i,j}=\max(f_{i,j},f_{i-w-1,k}-j*AP_i+k*AP_i)
fi,j=max(fi,j,fi−w−1,k−j∗APi+k∗APi)
稍微移一下项,得到:
f
i
,
j
=
max
(
f
i
,
j
,
f
i
−
w
−
1
,
k
+
k
∗
A
P
i
−
j
∗
A
P
i
)
f_{i,j}=\max(f_{i,j},f_{i-w-1,k}+k*AP_i-j*AP_i)
fi,j=max(fi,j,fi−w−1,k+k∗APi−j∗APi)
在枚举
i
,
j
i,j
i,j时,
(
−
j
∗
A
P
i
)
(-j*AP_i)
(−j∗APi)就是定值了,所以我们要使得
(
f
i
−
w
−
1
,
k
+
k
∗
A
P
i
)
(f_{i-w-1,k}+k*AP_i)
(fi−w−1,k+k∗APi)尽可能大,单调队列优化即可。
[ 4 ] [4] [4]情况同理。
就
O
K
OK
OK了。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,w;
int f[2010][2010],que[2010];
int main()
{
int ap,bp,as,bs;
scanf("%d %d %d",&n,&m,&w);
memset(f,-63,sizeof(f));
for(int i=1;i<=n;i++)
{
scanf("%d %d %d %d",&ap,&bp,&as,&bs);
for(int j=0;j<=as;j++)
f[i][j]=-j*ap;
for(int j=0;j<=m;j++)
f[i][j]=max(f[i][j],f[i-1][j]);
if(i<=w) continue;
int head=1,tail=0;
for(int j=0;j<=m;j++)
{
while(head<=tail&&que[head]<j-as) head++;
if(head<=tail) f[i][j]=max(f[i][j],f[i-w-1][que[head]]-(j-que[head])*ap);
while(head<=tail&&f[i-w-1][que[tail]]+que[tail]*ap<=f[i-w-1][j]+j*ap)
tail--;
que[++tail]=j;
}
head=1,tail=0;
for(int j=m;j>=0;j--)
{
while(head<=tail&&que[head]>j+bs) head++;
if(head<=tail) f[i][j]=max(f[i][j],f[i-w-1][que[head]]+(que[head]-j)*bp);
while(head<=tail&&f[i-w-1][que[tail]]+que[tail]*bp<=f[i-w-1][j]+j*bp)
tail--;
que[++tail]=j;
}
}
printf("%d",f[n][0]);
}