昨天晚上,初见它时,月黑风高,一个电脑,一支笔,一个人
今天秋高气爽,再一瞥,回眸间
题目
题解
首先这种
i
i
i 天与前面
j
j
j 天有关联,而且让你求最后一天的极值
我们一定会联想到
D
P
DP
DP
先搞定 d p dp dp 定义?
第一维毋庸置疑就是表示
i
i
i 天,接着思考一下第二维
j
j
j
1)如果表示
i
i
i 天买了或者卖了
j
j
j 股票,就遇到了一个问题,
i
−
W
−
1
i-W-1
i−W−1 天的时候股票数又是多少呢?
难道我要再去用一重循环枚举吗?显然不现实。
2)如果表示
i
i
i 天手上还拥有着
j
j
j 股票(
i
i
i 天已经操作完成了)
那么
±
k
±k
±k 就是
i
−
W
−
1
i-W-1
i−W−1 的股票数了
综上 d p [ i ] [ j ] : i dp[i][j]:i dp[i][j]:i 天手上的股票数为 j j j 时的最大收益。
最后输出
d
p
[
n
]
[
0
]
dp[n][0]
dp[n][0] 就可以了,
n
n
n 天的时候手上没有股票数肯定是最优解了,不然你手上捏着股票又买卖不了,拿来擦屁股吗??
找到转移方程式?
-
这一天闲得慌,不买也不卖
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ( 0 ≤ j ≤ M a x P ) dp[i][j]=dp[i-1][j] (0≤j≤MaxP) dp[i][j]=dp[i−1][j](0≤j≤MaxP)
-
这一天才开始玩股票,只能去购进股票
d p [ i ] [ j ] = − j ∗ a p i ( j − a s i ≤ k < j ) dp[i][j]=-j*ap_i(j-as_i≤k<j) dp[i][j]=−j∗api(j−asi≤k<j)
-
自由权比较大,手上有点资本,也要购进
d p [ i ] [ j ] = d p [ i − W − 1 ] [ k ] − ( j − k ) ∗ a p i ( j − a s i ≤ k < j ) dp[i][j]=dp[i-W-1][k]-(j-k)*ap_i(j-as_i≤k<j) dp[i][j]=dp[i−W−1][k]−(j−k)∗api(j−asi≤k<j)
-
该开始赚钱钱了,卖一点股票出去
d p [ i ] [ j ] = d p [ i − W − 1 ] [ k ] + ( k − j ) ∗ b p i ( j < k ≤ j + b s i ) dp[i][j]=dp[i-W-1][k]+(k-j)*bp_i(j<k≤j+bs_i) dp[i][j]=dp[i−W−1][k]+(k−j)∗bpi(j<k≤j+bsi)
在这四种情况下,取一个 m a x max max 即可。
那么为什么i-W-1就是当前最优解呢?
因为我们是一天一天递推过去的,当前最优解会被传递过去,
这也是我们DP需要完成的
分析这个 D P DP DP 的时间复杂度, O ( n 3 ) O(n^3) O(n3)。
所以必须对 k k k 优化
我们就去拆一拆 case3
case4
的
D
P
DP
DP 式
d
p
[
i
]
[
j
]
=
d
p
[
i
−
W
−
1
]
[
k
]
−
(
j
−
k
)
∗
a
p
i
(
j
−
a
s
i
≤
k
<
j
)
dp[i][j]=dp[i-W-1][k]-(j-k)*ap_i(j-as_i≤k<j)
dp[i][j]=dp[i−W−1][k]−(j−k)∗api(j−asi≤k<j)
⇓
\Downarrow
⇓
d
p
[
i
]
[
j
]
=
d
p
[
i
−
W
−
1
]
[
k
]
+
k
∗
a
p
i
−
j
∗
a
p
i
(
j
−
a
s
i
≤
k
<
j
)
dp[i][j]=dp[i-W-1][k]+k*ap_i-j*ap_i(j-as_i≤k<j)
dp[i][j]=dp[i−W−1][k]+k∗api−j∗api(j−asi≤k<j)
我们会发现
j
∗
a
p
i
j*ap_i
j∗api 是固定不变的。
也就是说真正影响 d p [ i ] [ j ] dp[i][j] dp[i][j] 的是 d p [ i − W − 1 ] [ k ] + k ∗ a p i dp[i-W-1][k]+k*ap_i dp[i−W−1][k]+k∗api
而且 d p [ i − W − 1 ] [ k ] + k ∗ a p i dp[i-W-1][k]+k*ap_i dp[i−W−1][k]+k∗api 越大越好。
发现这个方程的实质是 k ∈ [ j − a s i , j ) k∈[j-as_i,j) k∈[j−asi,j) 这个范围内取得的且只和 k k k 有关。
d p [ i ] [ j ] = d p [ i − W − 1 ] [ k ] + ( k − j ) ∗ b p i ( j < k ≤ j + b s i ) dp[i][j]=dp[i-W-1][k]+(k-j)*bp_i(j<k≤j+bs_i) dp[i][j]=dp[i−W−1][k]+(k−j)∗bpi(j<k≤j+bsi) ⇓ \Downarrow ⇓ d p [ i ] [ j ] = d p [ i − W − 1 ] [ k ] + k ∗ b p i − j ∗ b p i ( j < k ≤ j + b s i ) dp[i][j]=dp[i-W-1][k]+k*bp_i-j*bp_i(j<k≤j+bs_i) dp[i][j]=dp[i−W−1][k]+k∗bpi−j∗bpi(j<k≤j+bsi)
我们会发现 j ∗ b p i j*bp_i j∗bpi 是固定不变的。
也就是说真正影响 d p [ i ] [ j ] dp[i][j] dp[i][j] 的是 d p [ i − W − 1 ] [ k ] + k ∗ b p i dp[i-W-1][k]+k*bp_i dp[i−W−1][k]+k∗bpi
而且 d p [ i − W − 1 ] [ k ] + k ∗ b p i dp[i-W-1][k]+k*bp_i dp[i−W−1][k]+k∗bpi 越大越好。
发现这个方程的实质是 k ∈ ( j , j + b s i ] k∈(j,j+bs_i] k∈(j,j+bsi] 这个范围内取得的且只和k有关
结合以上两种情况:就会联想到我们的单调队列了。
维护 k k k 的范围而且从大到小,每次取队头 h e a d head head 进行更值
这样就转化成了 O ( n 2 ) O(n^2) O(n2)。
最后注意一下循环顺序
买股票就从小到大 0 ∼ M a x P 0\sim MaxP 0∼MaxP
卖股票就从大到小 M a x P ∼ 0 MaxP\sim 0 MaxP∼0
代码实现
#include <cstdio>
#include <iostream>
using namespace std;
#define MAXN 2005
int T, MaxP, W;
int ap, bp, as, bs;
int head, tail;
int deq[MAXN];
int dp[MAXN][MAXN];
int main() {
scanf ( "%d %d %d", &T, &MaxP, &W );
++ W;
dp[0][0] = -MAXN * MAXN;
for ( int i = 1;i <= MaxP;i ++ )
dp[0][i] = dp[0][i - 1];
for ( int i = 1;i <= T;i ++ ) {
scanf ( "%d %d %d %d", &ap, &bp, &as, &bs );
for ( int j = 0;j <= MaxP;j ++ )
dp[i][j] = dp[i - 1][j];
for ( int j = 0;j <= as;j ++ )
dp[i][j] = max ( dp[i][j], -j * ap );
if ( i > W ) {
head = 1, tail = 0;
for ( int j = 0;j <= MaxP;j ++ ) {
while ( head <= tail && deq[head] < j - as )
head ++;
while ( head <= tail && deq[tail] * ap + dp[i - W][deq[tail]] <= j * ap + dp[i - W][j] )
-- tail;
deq[++ tail] = j;
dp[i][j] = max ( dp[i][j], dp[i - W][deq[head]] + deq[head] * ap - j * ap );
}
head = 1, tail = 0;
for ( int j = MaxP;j >= 0;j -- ) {
while ( head <= tail && deq[head] > j + bs )
head ++;
while ( head <= tail && deq[tail] * bp + dp[i - W][deq[tail]] <= j * bp + dp[i - W][j] )
tail --;
deq[++ tail] = j;
dp[i][j] = max ( dp[i][j], dp[i - W][deq[head]] + deq[head] * bp - j * bp );
}
}
}
printf ( "%d", dp[T][0] );
return 0;
}