trade(单调队列+dp)

trade

时间限制:2000/1000 MS(Java / Others)
内存限制:32768/32768 K(Java / Others)
总提交内容:5395 已接受提交内容:1870

问题描述

最近, lxhgww l x h g w w 沉迷于股票,他在几天的研究后发现了一些规律。
他预测下一个 T T 天的股市。在第i天,您可以用价格 APi A P i 买入一只股票,或者卖出一只股票以获得 BPi B P i
还有一些其他的限制,一个可以在第i天最多买入 ASi A S i 只股票,最多卖出 BSi B S i 只股票。
两个交易日的间隔时间应该超过W天。也就是说,假设您在第i天交易(任何买入或卖出股票被视为交易),下一个交易日必须在第 i+W+1 ( i + W + 1 ) 天或之后。
更重要的是,任何时候都最多拥有 MaxP M a x P 股票。

在第一天之前, lxhgww l x h g w w 已经有无限的资金,但没有股票,当然他想从股市赚尽可能多的钱。所以问题出现了,他至多可以赚多少钱?

输入

第一行是整数 t t ,即案例编号。
每种情况的第一行是三个整数TMaxPW
0W,T2000,1MaxP2000 ( 0 ≤ W , T ≤ 2000 , 1 ≤ M a x P ≤ 2000 )
接下来的T行分别具有上面提到的四个整数 APiBPiASiBSi A P i , B P i , A S i , B S i
1BPiΔi1000,1ΔiBSiMaxP ( 1 ≤ B P i ≤ Δ i ≤ 1000 , 1 ≤ Δ i , B S i ≤ M a x P )

输出

lxhgww l x h g w w 最赚钱。

示例输入

1
5 2 0
2 1 1 1
2 1 1 1
3 2 1 1
4 3 1 1
5 4 1 1

示例输出

3

传送门!!!




解:

这道题比起修剪草坪要有点脑子了,不过还是单调队列优化dp。怎么单调队列?还是要推一波式子:

fi,j=max{fiw1,k(jk)APi}     (k<j) f i , j = m a x { f i − w − 1 , k − ( j − k ) ∗ A P i }           ( k < j )

fi,j=max{fiw1,k+(kj)BPi}     (k>j) f i , j = m a x { f i − w − 1 , k + ( k − j ) ∗ B P i }           ( k > j )

fi,j=max{fi1,j} f i , j = m a x { f i − 1 , j }

这里的三个式子前两个把 j j 提出来是可以使用单调队列优化的。因为max里与 fi,j f i , j 毫无关系了,就相当于区间求最值。一开始写的时候忘记了第三个转移,想到相邻的两天不会转移,结果忘记不买也是转移。然后初始化初始化了好久,老是有些地方没有初始化。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct lxy{
    int id;
    int x;
};
int f[2005][2005];
int T,n,w,maxp;
int ap,bp,as,bs,ans;
deque <lxy> d;

int main()
{
    scanf("%d",&T);
    while(T--)
    {
      scanf("%d%d%d",&n,&maxp,&w);
      for(int i=0;i<=n;i++)
        for(int j=0;j<=maxp;j++)
          f[i][j]=-0x3f3f3f3f;
      for(int i=1;i<=min(w+1,n);i++)
      {
        scanf("%d%d%d%d",&ap,&bp,&as,&bs);
        for(int j=0;j<=min(as,maxp);j++)
          f[i][j]=-ap*j;
        for(int j=0;j<=maxp;j++)
          f[i][j]=max(f[i-1][j],f[i][j]);
      }
      for(int i=w+2;i<=n;i++)
      {
        scanf("%d%d%d%d",&ap,&bp,&as,&bs);
        while(!d.empty()) d.pop_back();
        for(int j=0;j<=maxp;j++)
        {
            if(!d.empty()&&d.front().id<j-as)
              d.pop_front();
            while(!d.empty()&&d.back().x<=f[i-w-1][j]+j*ap)
              d.pop_back();
            lxy p;p.x=f[i-w-1][j]+j*ap;p.id=j;
            d.push_back(p);
            f[i][j]=max(f[i][j],d.front().x-j*ap);
        }
        while(!d.empty()) d.pop_back();
        for(int j=maxp;j>=0;j--)
        {
            if(!d.empty()&&d.front().id>j+bs)
              d.pop_front();
            while(!d.empty()&&d.back().x<=f[i-w-1][j]+j*bp)
              d.pop_back();
            lxy p;p.x=f[i-w-1][j]+j*bp;p.id=j;
            d.push_back(p);
            f[i][j]=max(f[i][j],d.front().x-j*bp);
        }
        for(int j=0;j<=maxp;j++)
          f[i][j]=max(f[i-1][j],f[i][j]);
      }
      printf("%d\n",f[n][0]);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值