从初始状态经过买入卖出操作达到末状态,要使末状态值最大。
很明显满足无后效性和最优子结构。考虑用动态规划来解决这个问题。
-首先思考状态。
很明显是
Θ
(
n
2
)
\frak{\Theta(n^2)}
Θ(n2)。
考虑第一维,稍有常识的人直觉来说是枚举天数
第二维不会是钱数,那应该和“股票的数量”有关——
股票只有这一种,直接记录当前持有多少支该股。
状态:
f
[
i
]
[
j
]
\frak{f[i][j]}
f[i][j]表示第
i
\frak{i}
i天手里有
j
\frak{j}
j支股票的最大收益
-然后思考转移。
当然,
f
[
i
]
[
j
]
\frak{f[i][j]}
f[i][j]的转移只需要考虑
f
[
1
∼
(
i
−
W
)
]
[
1
∼
M
a
x
P
]
\frak{f[1\thicksim(i-\mathcal{W})][1\thicksim \mathcal{MaxP}]}
f[1∼(i−W)][1∼MaxP]
一开始一定要先买入,这是初状态,直接赋值。转移分三种:
不买入&不卖出的情况:
f
[
i
]
[
j
]
=
m
a
x
{
f
[
i
]
[
j
]
,
f
[
i
−
1
]
[
j
]
}
\frak{f[i][j]=max\{f[i][j],f[i-1][j]\}}
f[i][j]=max{f[i][j],f[i−1][j]}
买入:
f
[
i
]
[
j
]
=
m
a
x
{
f
[
1
∼
(
i
−
W
−
1
)
]
[
k
]
−
(
j
−
k
)
×
A
P
i
}
\frak{f[i][j]=max\{f[1\thicksim(i-\mathcal{W}-1)][\mathcal{k}]-(j-\mathcal{k})×\mathcal{AP_\frak{i}}\}}
f[i][j]=max{f[1∼(i−W−1)][k]−(j−k)×APi}
卖出:
f
[
i
]
[
j
]
=
m
a
x
{
f
[
1
∼
(
i
−
W
−
1
)
]
[
k
]
+
(
k
−
j
)
×
B
P
i
}
\frak{f[i][j]=max\{f[1\thicksim(i-\mathcal{W}-1)][\mathcal{k}]+(\mathcal{k}-j)×\mathcal{BP_\frak{i}}\}}
f[i][j]=max{f[1∼(i−W−1)][k]+(k−j)×BPi}
并且由于有了不买入&不卖出的转移,有:
转移方程:
初状态(第一次买入):
f
[
i
]
[
j
]
=
−
j
A
P
i
\frak{f[i][j]=-j\mathcal{AP_\frak{i}}}
f[i][j]=−jAPi
不买入/卖出:
f
[
i
]
[
j
]
=
m
a
x
{
f
[
i
]
[
j
]
,
f
[
i
−
1
]
[
j
]
}
\frak{f[i][j]=max\{f[i][j],f[i-1][j]\}}
f[i][j]=max{f[i][j],f[i−1][j]}
买入:
f
[
i
]
[
j
]
=
m
a
x
{
f
[
i
−
W
−
1
]
[
k
]
−
(
j
−
k
)
×
A
P
i
}
\frak{f[i][j]=max\{f[i-\mathcal{W}-1][\mathcal{k}]-(j-\mathcal{k})×\mathcal{AP_\frak{i}}\}}
f[i][j]=max{f[i−W−1][k]−(j−k)×APi}
卖出:
f
[
i
]
[
j
]
=
m
a
x
{
f
[
i
−
W
−
1
]
[
k
]
−
(
j
−
k
)
×
B
P
i
}
\frak{f[i][j]=max\{f[i-\mathcal{W}-1][\mathcal{k}]-(j-\mathcal{k})×\mathcal{BP_\frak{i}}\}}
f[i][j]=max{f[i−W−1][k]−(j−k)×BPi}
当然这不能作为最终的式子:因为这个式子里面引入了
k
\frak{k}
k,时间上增加了枚举
δ
\frak{\delta}
δ的一维。
这样的话复杂度是
Θ
(
n
3
)
\frak{\Theta(n^3)}
Θ(n3)的,当然不行。
-如何优化?
两个转移方程形如:
f
(
i
,
j
)
=
o
p
t
{
f
(
i
,
k
)
+
a
(
j
,
k
)
}
\frak{f(i,j)=opt\{f(i,\mathcal{k})+a(j,\mathcal{k})\}}
f(i,j)=opt{f(i,k)+a(j,k)}
这不好优化。但是
a
(
j
,
k
)
\frak{a(j,\mathcal{k})}
a(j,k)可以拆成两部分,一部分只与
(
i
,
j
)
\frak{(i,j)}
(i,j)有关,一部分只与
i
,
k
\frak{i,\mathcal{k}}
i,k有关。
这样式子就变成了
f
(
i
,
j
)
=
o
p
t
{
f
(
i
,
k
)
+
a
(
i
,
j
)
}
\frak{f(i,j)=opt\{f(i,\mathcal{k})+a(i,j)\}}
f(i,j)=opt{f(i,k)+a(i,j)}。
注意到单调队列的基本形式是
f
′
(
i
′
)
=
o
p
t
{
f
′
(
j
′
)
+
a
′
(
i
′
)
}
\frak{f'(i')=opt\{f'(j')+a'(i')\}}
f′(i′)=opt{f′(j′)+a′(i′)}
现在推广到二维,可以把
(
i
,
j
)
\frak{(i,j)}
(i,j)看作
i
′
\frak{i'}
i′,把
(
i
,
k
)
\frak{(i,\mathcal{k})}
(i,k)看作
j
′
\frak{j'}
j′。方程符合单调队列形式,可以优化。
最终复杂度
Θ
(
n
2
)
\frak{\Theta(n^2)}
Θ(n2)。
-构造代码
·读入
·循环枚举天数
i
\frak{i}
i
·清空单调队列
·循环枚举股票数
j
\frak{j}
j
·初状态(第一次买入)
·不买入也不卖出
·买入
·更新
f
[
i
]
[
j
]
\frak{f[i][j]}
f[i][j]
·更新单调队列
·删除单调队列中过期部分(
j
−
A
S
i
>
k
\frak{j-\mathcal{AS_\frak{i}}>\mathcal{k}}
j−ASi>k)
·在单调队列中加入
f
[
i
−
w
−
1
]
[
j
]
+
j
A
P
i
\frak{f[i-w-1][j]+j\mathcal{AP_i}}
f[i−w−1][j]+jAPi
·卖出
·更新
f
[
i
]
[
j
]
\frak{f[i][j]}
f[i][j]
·更新单调队列
·删除单调队列中过期部分(
j
+
B
S
i
<
k
\frak{j+\mathcal{BS_\frak{i}}<\mathcal{k}}
j+BSi<k)
·在单调队列中加入$\frak{f[i-w-1][j]+j\mathcal{BP_i}}
步骤省略对多组数据的处理
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<queue>
#include<cctype>
using namespace std;
int T,N,P,W,AP,BP,AS,BS;
int F[2005][2005]={},q[2005]={},ql=1,qr=0,Ans=0;
int main()
{
scanf("%d",&T);
while(T--)
{
memset(F,0xcf,sizeof(F));
Ans=0;
scanf("%d%d%d",&N,&P,&W);
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<=P;++j)F[i][j]=max(F[i][j],F[i-1][j]);
if(i<=W)continue;
ql=1, qr=0;
for(int j=0;j<=P;++j)
{
while(ql<=qr&&j-AS>q[ql])++ql;
while(ql<=qr&&F[i-W-1][q[qr]]+q[qr]*AP<=F[i-W-1][j]+j*AP)--qr;
q[++qr]=j;
if(ql<=qr)F[i][j]=max(F[i][j],F[i-W-1][q[ql]]+(q[ql]-j)*AP);
}
ql=1, qr=0;
for(int j=P;j>=0;--j)
{
while(ql<=qr&&j+BS<q[ql])++ql;
while(ql<=qr&&F[i-W-1][q[qr]]+q[qr]*BP<=F[i-W-1][j]+j*BP)--qr;
q[++qr]=j;
if(ql<=qr)F[i][j]=max(F[i][j],F[i-W-1][q[ql]]+(q[ql]-j)*BP);
}
}
for(int i=0;i<=P;++i)Ans=max(Ans,F[N][i]);
printf("%d\n",Ans);
}
return 0;
}