bzoj1855: [Scoi2010]股票交易

Description

最近lxhgww又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。 通过一段时间的观察,lxhgww预测到了未来T天内某只股票的走势,第i天的股票买入价为每股APi,第i天的股票卖出价为每股BPi(数据保证对于每个i,都有APi>=BPi),但是每天不能无限制地交易,于是股票交易所规定第i天的一次买入至多只能购买ASi股,一次卖出至多只能卖出BSi股。 另外,股票交易所还制定了两个规定。为了避免大家疯狂交易,股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔W天,也就是说如果在第i天发生了交易,那么从第i+1天到第i+W天,均不能发生交易。同时,为了避免垄断,股票交易所还规定在任何时间,一个人的手里的股票数不能超过MaxP。 在第1天之前,lxhgww手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,T天以后,lxhgww想要赚到最多的钱,聪明的程序员们,你们能帮助他吗?
Input

输入数据第一行包括3个整数,分别是T,MaxP,W。 接下来T行,第i行代表第i-1天的股票走势,每行4个整数,分别表示APi,BPi,ASi,BSi。
Output

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

5 2 0

2 1 1 1

2 1 1 1

3 2 1 1

4 3 1 1

5 4 1 1

Sample Output

3

HINT

对于30%的数据,0 < =W 对于50%的数据,0 < =W 对于100%的数据,0 < =W
对于所有的数据,1 < =BPi < =APi < =1000,1 < =ASi,BSi < =MaxP

题解

这题就直接DP一下就好了
f[i][j]表示前i填手里有j个股票的最优代价
然后每一次就枚举上一次有多少就好了
裸DP是这样子的(我拆了式子)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int N=2505;
const int MAX=1<<30;
int n,P,w;//天数   最大股票数   至少隔w天 
struct qq
{
    int a1,b1;//买卖钱数
    int a2,b2;//买卖数量 
}s[N];
int f[N][N];//最后一次交易在前i天,然后手上有j支股票的最大价值 
int main()
{
    scanf("%d%d%d",&n,&P,&w);
    for (int u=1;u<=n;u++)scanf("%d%d%d%d",&s[u].a1,&s[u].b1,&s[u].a2,&s[u].b2);
    for (int u=0;u<=n;u++)
        for (int i=0;i<=P;i++)
            f[u][i]=-MAX;
    f[0][0]=0;
    for (int u=1;u<=n;u++)//最后一天在哪一天交易
    {
        for (int i=0;i<=P;i++)//手里有多少股票 
        {
            int a=-MAX;
            for (int j=max(0,i-s[u].a2);j<i;j++)//上一次有多少 
                a=max(a,f[max(u-w-1,0)][j]+j*s[u].a1);
            a-=(s[u].a1*i);
            f[u][i]=max(f[u][i],a);

            a=-MAX;
            for (int j=i+1;j<=min(i+s[u].b2,P);j++)//上一次有多少   买的
                a=max(a,f[max(u-w-1,0)][j]+s[u].b1*j); 
            a-=(s[u].b1*i);
            f[u][i]=max(f[u][i],a);

            f[u][i]=max(f[u][i],f[u-1][i]);
        }
    }
    int ans=0;
    for (int u=0;u<=P;u++) ans=max(ans,f[n][u]);
    printf("%d\n",ans);
    return 0;
}

不难可以发现,这个式子绝对是可以单调队列的。。于是我就很高兴地开打了
于是我得到了下面这个代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=2505;
const int MAX=1<<30;
int n,P,w;//天数   最大股票数   至少隔w天 
struct qq
{
    int a1,b1;//买卖钱数
    int a2,b2;//买卖数量 
}s[N];
int f[N][N];//最后一次交易在前i天,然后手上有j支股票的最大价值
struct qt
{
    int x,y;//权值   位置 
    bool operator < (qt a) const
    {
        return x<a.x;
    };
};
int q[N];//单调队列
int q1[N];//单调队列 
int main()
{
    scanf("%d%d%d",&n,&P,&w);
    for (int u=1;u<=n;u++)scanf("%d%d%d%d",&s[u].a1,&s[u].b1,&s[u].a2,&s[u].b2);
    for (int u=0;u<=n;u++)
        for (int i=0;i<=P;i++)
            f[u][i]=-MAX;
    f[0][0]=0;
    for (int u=1;u<=n;u++)//最后一天在哪一天交易
    {
        int k=max(u-w-1,0);
        int st=1,ed=0;//第一个东西的单调队列
        int st1=1,ed1=0;//第二个东西的单调队列

        for (int i=0;i<=P;i++)//手里有多少股票
        {
            int a=-MAX;
            int ooo=max(0,i-s[u].a2),ooo1=i-1;
            while (st<=ed&&q[st]<ooo) st++;
            if (ooo1>=0)
            while (st<=ed&&f[k][q[ed]]+q[ed]*s[u].a1<=f[k][ooo1]+ooo1*s[u].a1) ed--;
            q[++ed]=ooo1;
            if (st<=ed) a=f[k][q[st]]+q[st]*s[u].a1;
            a-=(s[u].a1*i);
            f[u][i]=max(f[u][i],a);

            a=-MAX;
            ooo=i+1,ooo1=min(i+s[u].b2,P);//下界
            while (st1<=ed1&&q1[st1]<ooo) st1++;
            if (ooo1>=ooo) 
            {
            while (st1<=ed1&&f[k][q1[ed1]]+q1[ed1]*s[u].b1<=f[k][ooo1]+ooo1*s[u].b1) ed1--;
            q1[++ed1]=ooo1;
            }
            if (st1<=ed1) a=f[k][q1[st1]]+q1[st1]*s[u].b1;
            a-=(s[u].b1*i);

            f[u][i]=max(f[u][i],a);

            f[u][i]=max(f[u][i],f[u-1][i]);
        }
    }
    int ans=0;
    for (int u=0;u<=P;u++) ans=max(ans,f[n][u]);
    printf("%d\n",ans);
    return 0;
}

看起来很对是不是,然而对拍就是过不了QAQ
检查发现,是q1的单调队列GG了。。
要是有谁能马上反应出为什么,那他肯定是大佬,比我高到不知道哪里去了

于是我就调这个傻逼题调了非常非常久

最后发现,q1当i=0的时候,你有许多点都没有入队
因此要加上下面这段话
在进入到第二重循环里面之前

for (int ooo1=1;ooo1<min(s[u].b2,P);ooo1++)
        {
            while (st1<=ed1&&q1[st1]<1) st1++;
            while (st1<=ed1&&f[k][q1[ed1]]+q1[ed1]*s[u].b1<=f[k][ooo1]+ooo1*s[u].b1) ed1--;
            q1[++ed1]=ooo1;
        }

吧一些点可行的点入队,才可以过

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值