Trade-----HDU3401----单调队列优化的DP

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3401

题目意思:

有T天,你每天可以以API买进,BPI卖出,最多买ASI个,最多卖BSI个

最多只能持有MAXP个商品,相邻两次交易要大于W天

问你最多在第T天结束的时候能赚多少钱

解题思路:

有三个状态转移方程,其中dp[i][j]表示第i天手上有j个商品时我赚的钱

dp[i][j] = max(dp[i][j],dp[i-1][j])   //即针对前一天我啥也不做

对于买而言,有一个递推式

dp[i][j] = max(dp[i][j],max(dp[i-w-1][k] - (j-k)*buyp[i]  )

进一步拆开就是 dp[i][j] = max(dp[i][j], max(dp[i-w-1][k]+k*buyp[i]) - j*buyp[i] );

因为是买,所以0<=(j-k)<=canbuyn[i]

我们可以利用优先队列将对于i来说能看到的最大放在队首

对于卖而言同理,只不过方程变成

dp[i][j] = max(dp[i][j], max(dp[i-w-1][k]+k*sellp[i])-j*sellp[i]

0<=(k-j)<=canselln[i]

下面上代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;

const int maxn = 3000;

struct node
{
    int val,pos;
}q[maxn];

int sellp[maxn],buyp[maxn];
int canselln[maxn],canbuyn[maxn];
int dp[maxn][maxn];
int T,n,maxp,w;
const int inf = 0x3f3f3f3f;

void dpf()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=maxp;j++)
            dp[i][j] = -inf;
    }

    //在前w+1天不能卖,只能买
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=min(maxp,canbuyn[i]);j++)
        {
            dp[i][j] = -buyp[i]*j;  //买的都是负账
        }
    }

    for(int i=2;i<=n;i++) //针对前一天我啥也不做
    {
        for(int j=0;j<=maxp;j++)
            dp[i][j] = max(dp[i][j],dp[i-1][j]);
    }

    for(int i=w+2;i<=n;i++)   //前w+1天除了买啥也不能做,既能买又能卖得从w+2开始
    {
        int pre = i-w-1;
        //对于买而言,有一个递推式
        //dp[i][j] = max(dp[i][j],max(dp[i-w-1][k] - (j-k)*buyp[i]  )
        //进一步拆开就是 dp[i][j] = max(dp[i][j], max(dp[i-w-1][k]+k*buyp[i]) - j*buyp[i] );
        //因为是买,所以0<=(j-k)<=canbuyn[i]
        //我们可以利用优先队列将对于i来说能看到的最大放在队首
        int head,tail;
        head = 0;
        tail = -1;
        for(int j=0;j<=maxp;j++)
        {
            dp[i][j] = max(dp[i][j],dp[i-1][j]);
            node tmp;
            tmp.val = dp[pre][j]+j*buyp[i];
            tmp.pos = j;
            //插入到优先队列里面去
            while(head<=tail && q[tail].val < tmp.val) tail--;
            q[++tail] = tmp;
            //队首还要满足0<=(j-k)<=canbuyn[i]
            while(head<=tail && (j-q[head].pos)>canbuyn[i] )head++;

            //取队首
            if(head<=tail) dp[i][j] = max(dp[i][j],q[head].val-j*buyp[i]);
        }

        //对于卖而言同理,只不过方程变成
        //dp[i][j] = max(dp[i][j], max(dp[i-w-1][k]+k*sellp[i])-j*sellp[i]
        //0<=(k-j)<=canselln[i]
        head=0;
        tail=-1;
        for(int j=maxp;j>=0;j--)
        {
            dp[i][j] = max(dp[i][j],dp[i-1][j]);
            node tmp;
            tmp.val = dp[pre][j]+j*sellp[i];
            tmp.pos = j;

            while(head<=tail && q[tail].val < tmp.val) tail--;
            q[++tail] = tmp;

            while(head<=tail && (q[head].pos-j) > canselln[i]) head++;

            if(head<=tail) dp[i][j] = max(dp[i][j],q[head].val-j*sellp[i]);

        }


    }

}


int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&maxp,&w);
        for(int i=1;i<=n;i++)
            scanf("%d%d%d%d",&buyp[i],&sellp[i],&canbuyn[i],&canselln[i]);
        dpf();
        int ans = 0;
        for(int j=0;j<=maxp;j++)
            ans = max(ans,dp[n][j]);
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值