洛谷 P2569[SCOI2010]股票交易(动规+单调队列)

//只能写出裸的动规,为什么会有人能想到用单调队列优化Orz

题目描述

最近lxhgww又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。

通过一段时间的观察,lxhgww预测到了未来T天内某只股票的走势,第i天的股票买入价为每股APi,第i天的股票卖出价为每股BPi(数据保证对于每个i,都有APi>=BPi),但是每天不能无限制地交易,于是股票交易所规定第i天的一次买入至多只能购买ASi股,一次卖出至多只能卖出BSi股。

另外,股票交易所还制定了两个规定。为了避免大家疯狂交易,股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔W天,也就是说如果在第i天发生了交易,那么从第i+1天到第i+W天,均不能发生交易。同时,为了避免垄断,股票交易所还规定在任何时间,一个人的手里的股票数不能超过MaxP。

在第1天之前,lxhgww手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,T天以后,lxhgww想要赚到最多的钱,聪明的程序员们,你们能帮助他吗?

输入输出格式

输入格式:

输入数据第一行包括3个整数,分别是T,MaxP,W。

接下来T行,第i行代表第i-1天的股票走势,每行4个整数,分别表示APi,BPi,ASi,BSi。

输出格式:

输出数据为一行,包括1个数字,表示lxhgww能赚到的最多的钱数。

输入输出样例

输入样例#1:
5 2 0
2 1 1 1
2 1 1 1
3 2 1 1
4 3 1 1
5 4 1 1
输出样例#1:
3

说明

对于30%的数据,0<=W<T<=50,1<=MaxP<=50

对于50%的数据,0<=W<T<=2000,1<=MaxP<=50

对于100%的数据,0<=W<T<=2000,1<=MaxP<=2000

对于所有的数据,1<=BPi<=APi<=1000,1<=ASi,BSi<=MaxP

题解:

裸的方程:

令每行四个数a,b,c,d

方程f[i][j]表示第 i 天结束后,手里剩下 j 股的最大利润
则 不买不卖:f[i][j]=f[i-1][j]
买入: f[i][j]=max{f[i-w-1][k]-a[i]*(j-k)}(j-c[i]<=k<=j-1)
卖出: f[i][j]=max{f[i-w-1][k]+b[i]*(k-j)}(j+1<=k<=j+d[i])

正确的方法(迷之变形):

买入:f[i][j]=max{f[i-w-1][k]+k*a[i]}-a[i]*j
卖出:f[i][j]=max{f[i-w-1][k]+k*b[i]}-b[i]*j
所以可以将括号里面的方程式压入单调队列

此时复杂度降低为复杂度O(N*W)原来O(N*W*2)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define man 2010
int a[man],b[man],c[man],d[man],f[man][man],sta[man];
int t,mp,w,hd,tl;
int main()
{
    cin>>t>>mp>>w;
    for(int i=1;i<=t;i++) cin>>a[i]>>b[i]>>c[i]>>d[i];
    memset(f,-127/3,sizeof(f));
    for(int i=0;i<=t;i++)f[i][0]=0;
    for(int i=1;i<=t;i++)
    {
        for(int j=0;j<=c[i];j++)f[i][j]=-a[i]*j;
        for(int j=mp;j>=0;j--)f[i][j]=max(f[i][j],f[i-1][j]);
        if(i-w-1>=0)
        {
            hd=1;tl=0;
            for(int j=0;j<=mp;j++)
            {
                while(hd<=tl&&sta[hd]<j-c[i])hd++;
                while(hd<=tl&&f[i-w-1][j]+a[i]*j>=f[i-w-1][sta[tl]]+a[i]*sta[tl])tl--;
                sta[++tl]=j;
                if(hd<=tl)
                f[i][j]=max(f[i][j],f[i-w-1][sta[hd]]-a[i]*(j-sta[hd]));
                }
            hd=1;tl=0;
            for(int j=mp;j>=0;j--)
            {
                while(hd<=tl&&sta[hd]>j+d[i])hd++;
                while(hd<=tl&&f[i-w-1][j]+b[i]*j>=f[i-w-1][sta[tl]]+b[i]*sta[tl])tl--;
                sta[++tl]=j;
                if(hd<=tl)
                f[i][j]=max(f[i][j],f[i-w-1][sta[hd]]+b[i]*(sta[hd]-j));
                }
                }
        }
    int ans=0;
    for(int i=0;i<=mp;i++)ans=max(ans,f[t][i]);
    cout<<ans<<endl;
    }

话说,为什么大家的题解几乎都是一样的。。(我也是看别人的。。。)

原文地址:https://www.cnblogs.com/Slager-Z/p/7766301.html