HDU3401 Trade (动态规划+单调队列)

本文介绍了一种基于单调队列优化的动态规划算法解决特定条件下的股票买卖问题。该算法能在O(n)的时间复杂度内求解出在限制交易频率、持有股票数量及交易量的情况下,如何最大化收益。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
知道之后n天的股票买卖价格(api,bpi),以及每天股票买卖数量上限(asi,bsi),问他最多能赚多少钱。开始时有无限本金,要求任两次交易需要间隔W天以上,即第i天交易,第i+w+1天才能再交易。同时他任意时刻最多只能拥有maxp的股票,
容易写出DP方程  dp[i][j]=max{dp[i-1][j],max{dp[r][k]-APi[i]*(j-k)}(0<r<i-w,k<j),max{dp[r][k]+BPi[i]*(k-j)}(0<r<i-w,k>j)} 分别是第i天不交易,买股票和卖股票3种转移。
第一眼看会发现这个方程十分冗杂,没有下手的地方。
我们可以分析下,对于买卖股票的转移中的dp[r][k]中的r是否有枚举的价值,注意dp[i][j]中取最大时会考虑dp[i-1][j]的值,也就是dp[i-w-1][k]>=dp[r][k](0<r<i-w)恒成立。这样就省去了r 的枚举,但这还不够。
以买股票为例,我们可以化简max{dp[i-w-1][k]-APi[i]*(j-k)}(k<j)},即dp[i-w-1][k]-APi[i]*(j-k)=dp[i-w-1][k]+APi[i]*k-APi[i]*j(k<j),可以发现对于确定的i和j只要取符合条件中最大的dp[i-w-1][k]+APi[i]*k即可,这个可以通过单调队列实现。
*/
/*
单调队列也就是分买入和卖出两种情况,对每一个i维护一个单调队列,这样可以在O(n)时间固定一个i,更新出dp[i][j]
*/
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const double pi=acos(-1);
const double EPS=1e-10;
const int MAXN=2000+10;
const int INF=1e9+7;
int T;
typedef long long ll;
int ap[MAXN],bp[MAXN],as[MAXN],bs[MAXN];
int dp[MAXN][MAXN];
struct node
{
    int x;//存dp[i-w-1][k]+APi[i]*k或dp[i-w-1][k]+BPi[i]*k
    int p;//当前持股数
} q[2005],temp;
int s,e;

int main(){
    scanf("%d",&T);
    while(T--){
        int t,maxp,w;
        scanf("%d%d%d",&t,&maxp,&w);
        for(int i=1;i<=t;i++){
            scanf("%d%d%d%d",ap+i,bp+i,as+i,bs+i);
        }
        for(int i=0;i<=t;i++)
            for(int j=0;j<=maxp;j++)
                dp[i][j]=-INF;
        for(int i=1;i<=t;i++){
            for(int j=0;j<=maxp;j++)
                if(j<=as[i])
                    dp[i][j]=max(dp[i-1][j],-j*ap[i]);
                else
                    dp[i][j]=dp[i-1][j];
            if(i-w-1<=0)
                continue;
            //买入
            s=e=1;
            for(int j=0;j<=maxp;j++){
                temp.p=j;
                temp.x=dp[i-w-1][j]+ap[i]*j;
                //入队
                for(;s<e&&q[e-1].x<temp.x;e--);
                q[e++]=temp;
                //找最大值
                for(;s<e&&q[s].p+as[i]<j;s++);
                dp[i][j]=max(dp[i][j],q[s].x-ap[i]*j);
            }
            s=e=1;
            //卖出
            for(int j=maxp;j>=0;j--){
                temp.p=j;
                temp.x=dp[i-w-1][j]+bp[i]*j;
                for(;s<e&&q[e-1].x<temp.x;e--);
                q[e++]=temp;
                for(;s<e&&q[s].p-bs[i]>j;s++);
                dp[i][j]=max(dp[i][j],q[s].x-bp[i]*j);
            }
        }
        int ans=0;
        for(int i=0;i<=maxp;i++){
            ans=max(ans,dp[t][i]);
        }
        printf("%d\n",ans);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值