题目传送门
题意:
初始时你没有股票。
第 天股票购买单价 ,卖出单价是 。
第 天最多购买 个股票,最多卖出 个股票。
你持有股票个数在任何时刻不能超过 个。
买股票或卖股票都称为交易。
假如第 天你进行了交易,那么下次最早交易时间是第 天。即两次交易至少间隔 天。
问你 天后,你最多拥有多少钱。
数据范围: , 。
, 。
题解:
这个应该能想到是dp。
表示第 天结束时你有 个股票时拥有的最大钱数。
分四种情况讨论:
(1)第 天凭空买股票,就是前 天不进行交易,第 天你买股票。
(2)第 天不进行交易。
(3)前面进行过交易,第 天买股票。
其中 是有范围的, 。
(4)前面进行过交易,第 天卖股票。
其中 是有范围的, 。
直接暴力计算时间复杂度是 ,接受不了。
所以要优化。考虑单调队列优化。
每次计算第 天的状态时,我们维护第 的状态的单调队列,在所需范围内寻找最大值即可。
单调队列优化后时间复杂度是 ,可以接受。
感受:
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
typedef pair<int , int> pii ;
const int maxn = 2005 ;
int t , maxp , w ;
int ap , bp , as , bs ;
int dp[maxn][maxn] ;
int q[maxn] ;
//deque<int> q ;
int main()
{
scanf("%d%d%d" , &t , &maxp , &w) ;
for(int i = 0 ; i <= t ; i ++)
for(int j = 0 ; j <= maxp ; j ++)
dp[i][j] = -2e9 ;
dp[0][0] = 0 ;
for(int i = 1 ; i <= t ; i ++)
{
scanf("%d%d%d%d" , &ap , &bp , &as , &bs) ;
//凭空买
for(int j = 0 ; j <= as ; j ++)
dp[i][j] = -ap * j ;
//不买也不卖
for(int j = 0 ; j <= maxp ; j ++)
dp[i][j] = max(dp[i][j] , dp[i - 1][j]) ;
if(i - w - 1 < 1) continue ;
int l = 1 , r = 0;
for(int j = 0 ; j <= maxp ; j++)
{
while(l <= r && q[l] < j - as)
l++;
while(l <= r &&
dp[i - w - 1][q[r]] + q[r] * ap
<=
dp[i - w - 1][j] + j * ap)
r--;
q[++r] = j;
if(l <= r)
dp[i][j] = max(dp[i][j] , dp[i - w - 1][q[l]] +
q[l] * ap - j * ap);
}
l = 1 , r = 0;
for(int j = maxp ; j >= 0 ; j--){
while(l <= r && q[l] > j + bs)
l++;
while(l <= r && dp[i - w - 1][q[r]] + q[r] * bp <=
dp[i - w - 1][j] + j * bp)
r--;
q[++r] = j;
if(l <= r)
dp[i][j] = max(dp[i][j] , dp[i - w - 1][q[l]] +
q[l] * bp - j * bp);
}
}
int ans = 0 ;
for(int i = 0 ; i <= maxp ; i ++) ans = max(ans , dp[t][i]) ;
printf("%d\n" , ans) ;
return 0 ;
}