[hdu3401]trade——单调队列优化DP

题目大意:

知道之后n天的股票买卖价格(api,bpi),以及每天股票买卖数量上限(asi,bsi),问他最多能赚多少钱。开始时有无限本金,要求任两次交易需要间隔W天以上,即第i天交易,第i+w+1天才能再交易。同时他任意时刻最多只能拥有maxp的股票。

思路:

dp方程很好想,一开始想出来是四次方的,但是发现不用枚举上一天是哪一天,因为交易只和今天有关,后面的状态肯定包括前面等待天数不买的状态,所以就变成三次方的了。

方程:

dp[i][j]=max(dp[i1][j]) d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] )
dp[i][j]=max(dp[iw1][k]+kBp[i]jBp[i])(j<=k<=j+Bs[i]) d p [ i ] [ j ] = m a x ( d p [ i − w − 1 ] [ k ] + k ∗ B p [ i ] − j ∗ B p [ i ] ) ( j <= k <= j + B s [ i ] )
dp[i][j]=max(dp[iw1][k]+kAp[i]jAp[i])(jAs[i]<=k<=j) d p [ i ] [ j ] = m a x ( d p [ i − w − 1 ] [ k ] + k ∗ A p [ i ] − j ∗ A p [ i ] ) ( j − A s [ i ] <= k <= j )

发现对于每个固定的j,最大值都是一个与k有关的函数,同一个i不同j之间的k函数是共享的,所以对于dp[i][j]的取值是可以用单调队列维护来维护这个与k有关的函数的,时间就这样降到二次方了。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<climits>
#include<deque>
using namespace std;
void File(){
    freopen("[hdu3401]trade.in","r",stdin);
    freopen("[hdu3401]trade.out","w",stdout);
}
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
const int maxn=2000+10;
int T,t,maxp,w,ap[maxn],bp[maxn],as[maxn],bs[maxn];
int dp[maxn][maxn];
deque<int>qu;
void Push(int bd,int x,int pp){
    while(qu.size() && dp[bd][qu.back()]+qu.back()*pp<=dp[bd][x]+x*pp)
        qu.pop_back();
    qu.push_back(x);
}
void Pop(int x){
    while(qu.size() && qu.front()<x)
        qu.pop_front();
}
void work(){
    REP(i,0,as[1])dp[1][i]=-i*ap[1];
    REP(i,2,w+1)REP(j,0,maxp){
        chkmax(dp[i][j],dp[i-1][j]);
        if(j<=as[i])chkmax(dp[i][j],-j*ap[i]);
    }
    REP(i,w+2,t){
        REP(j,0,maxp)chkmax(dp[i][j],dp[i-1][j]);
        int bd=i-w-1,up=min(bs[i]-1,maxp);
        qu.clear();
        REP(j,0,up)Push(bd,j,bp[i]);
        REP(j,0,maxp){
            if(j+bs[i]<=maxp)Push(bd,j+bs[i],bp[i]);
            Pop(j);
            chkmax(dp[i][j],dp[bd][qu.front()]+qu.front()*bp[i]-j*bp[i]);
        }
        qu.clear();
        REP(j,0,maxp){
            Push(bd,j,ap[i]);
            Pop(j-as[i]);
            chkmax(dp[i][j],dp[bd][qu.front()]+qu.front()*ap[i]-j*ap[i]);
        }
    }
    printf("%d\n",dp[t][0]);
}
int main(){
    File();
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&t,&maxp,&w);
        REP(i,1,t)scanf("%d%d%d%d",&ap[i],&bp[i],&as[i],&bs[i]);
        REP(i,1,t)REP(j,0,maxp)dp[i][j]=-inf;
        work();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值