zoukankan      html  css  js  c++  java
  • [SCOI2010]股票交易

    题意

    题目描述

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

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

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

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

    输入输出格式

    输入格式:

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

    接下来 $T$ 行,第 $i$ 行代表第 $i-1$ 天的股票走势,每行 $4$ 个整数,分别表示 $AP_i, BP_i, AS_i, BS_i$。

    输出格式:

    输出数据为一行,包括 $1$ 个数字,表示 $ ext{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\%$ 的数据,$0leq W<Tleq 50,1leq ext{MaxP}leq50$

    对于 $50\%$ 的数据,$0leq W<Tleq 2000,1leq ext{MaxP}leq50$

    对于 $100\%$ 的数据,$0leq W<Tleq 2000,1leq ext{MaxP}leq2000$

    对于所有的数据,$1leq BP_ileq AP_ileq 1000,1leq AS_i,BS_ileq ext{MaxP}$

    分析

    参照Sooke的题解,现在还有这种把思考过程放在网上的好人。

    首先,不难想到本题可以用动态规划来解,这里就省略是如何想到动态规划的了。

    动态规划的状态应该如何设?

    观察数据范围,我们发现开一个 2000 × 2000 的数组来表示状态是没有问题的。

    第一个维度,绝对用来表示是第几天,这个不用多想。

    第二个维度,就需要稍微思考思考,我们发现它应该记录拥有多少张股票。

    结合一下,整理出: (f_{i,j}) 表示第(i)天后拥有(j)张股票可以赚到的最多钱数。

    动态规划的转移方程应该如何列?状态的初值怎么设?

    分情况讨论。某一天,要么买股,要么卖股,要么不买也不卖。而买股又可分为两个情况,要么凭空买,跟前面交不交易没有任何关系,要么在前面交易的基础上买股,通俗一点说,也就是该状态是否需要由前面的状态转移而来。

    那为什么卖股不用分为两个情况呢?仔细想想,怎么能凭空卖呢?既然和前面的贸易没有关系,也就是说手上没有任何一张股票,哪来的股票用来卖?

    因此,一共有四种情况,其中只有一个情况并不需要其他状态转移,单独讨论:

    1. 凭空买

    仅本情况下的状态可以直接赋值,其他状态初值为 -inf,即:

    (f_{i,j}=-ap_i imes j)

    ((0 leqslant j leqslant as_i))

    下面三种情况,分别可列三个状态转移方程:

    2. 不买也不卖

    最好想也最好列转移方程的一种情况,直接由上一天转移,且拥有股票数量不变。即:

    (f_{i,j})=(max(f_{i,j} \,, \, f_{i-1,j}))

    3. 在之前的基础上买股票

    这里的方程就比较复杂了,这里详细解释一下:

    题目中指明说两次交易需要至少隔 (w) 天。也就是说,今天是第 (i) 天交易,那么上一次交易最近是在第 (i - w - 1) 天。

    可我第 (i - w - 1) 天之前也可以交易啊?为什么偏偏是那一天,而不是第 (i - w - 2) 天?

    回看刚刚讨论的第 2 种情况,我们已经把某一天以前的最优答案转移到了该天,所以从那一天转移,相当于从那一天包括前面任何一天开始转移,省去了大把时间。

    接下来,我们已知第 (i) 天拥有 (j) 张股票,假设第 (i - w - 1) 天拥有 (k) 张股票。

    (k) 一定比 (j) 小,因为这一种情况是买股票,股票只能多。

    但股票又不能买太多,限制最多为 (as) 张。所以 (j - as_i) 是底线。

    继续,这次交易买了 (j - k) 张股票,要花去 ((j - k) imes ap_i) 元。

    整理一下,转移方程为:

    (f_{i,j})=(max(f_{i,j} \,, \,f_{i-w-1,k}-(j-k) imes ap_i))

    ((j - as_i leqslant k < j))

    4. 在之前的基础上卖股票

    和上面的情况特别类似,在此简单地说一下区别。

    因为是卖股票,(k) 这次要比 (j) 大。但要小于等于 (j + bs_i)

    这次交易卖了 (k - j) 张邮票,赚得 ((k - j) imes bp_i) 元。

    整理一下,转移方程为:

    (f_{i,j})=(max(f_{i,j} \,, \,f_{i-w-1,k}+(k-j) imes bp_i))

    ((j < k leqslant j + bs_i))

    为什么可以使用单调性优化?

    上面的 3,4 两种情况,列起来头头是道,但我们深入计算,发现时间复杂度是三次方级的。很显然,我们要想尽办法,把时间复杂度降到二次方级。

    这个时候发现那两种情况,实际上可以使用单调性优化。此时达到降时间复杂度的目标。

    就以第 3 种情况,在之前的基础上买股票为例子吧。

    再重复提一下那个转移方程:

    (f_{i,j})=(max(f_{i,j} \,, \,f_{i-w-1,k}-(j-k) imes ap_i))

    ((j - as_i leqslant k < j))

    运用乘法分配率:

    (f_{i,j})=(max(f_{i,j} \,, \,f_{i-w-1,k}-j imes ap_i+k imes ap_i))

    ((j - as_i leqslant k < j))

    既然要将状态转移给 (f_{i,j}),此时 (j) 可以从 (max) 中提取出,此时转移方程变为:

    (f_{i,j})=(max(f_{i,j} \,, \,f_{i-w-1,k}+k imes ap_i)-j imes ap_i)

    ((j - as_i leqslant k < j))

    此时,我们发现以上的转移方程符合单调性优化的条件,故可以使用单调性优化。

    没有理解地,可以这么想:由于转移有这么一个区间前提:((j - as_i leqslant k < j)),又因为转移方程中出现了 max,也就是说转移的目的是从该区间中,找到某个最大的值,基础好的你应该已经发现这就是滑动窗口了吧?只不过放进单调队列的元素,是 (f_{i-w-1,k}+k imes ap_i),但这并不影响转移。

    那么第 4 种情况的转移,我就不多解释了,它的转移方程可以整理为:

    (f_{i,j})=(max(f_{i,j} \,, \,f_{i-w-1,k}+k imes bp_i)-j imes bp_i)

    ((j < k leqslant j + bs_i))

    好像除了后面的转移区间要求,两个整理后的方程几乎都是一样的。

    对了,还有一个细节,是第 3 种情况转移应该顺序,第 4 种情况转移应该逆序,这个不难理解,先自己想想吧。

    时间复杂度(O(n^2))

    代码

    #include<bits/stdc++.h>
    #define rg register
    #define il inline
    #define co const
    template<class T>il T read(){
        rg T data=0,w=1;rg char ch=getchar();
        while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
        while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
        return data*w;
    }
    template<class T>il T read(rg T&x) {return x=read<T>();}
    typedef long long ll;
    using namespace std;
    
    co int N=2e3+1;
    int f[N][N],q[N];
    int main(){
    	int t,maxp,w;
    	read(t),read(maxp),read(w);
    	memset(f,0xcf,sizeof f);
    	for(int i=1;i<=t;++i){
    		int ap,bp,as,bs;
    		read(ap),read(bp),read(as),read(bs);
    		for(int j=0;j<=as;++j) f[i][j]=-j*ap;
    		for(int j=0;j<=maxp;++j) f[i][j]=max(f[i][j],f[i-1][j]);
    		if(i<=w) continue;
    		int l=1,r=0; // empty set
    		for(int j=0;j<=maxp;++j){
    			while(l<=r&&q[l]<j-as) ++l;
    			while(l<=r&&f[i-w-1][q[r]]+q[r]*ap<=f[i-w-1][j]+j*ap) --r;
    			q[++r]=j;
    			if(l<=r) f[i][j]=max(f[i][j],f[i-w-1][q[l]]+(q[l]-j)*ap);
    		} // here judge is not essential
    		l=1,r=0;
    		for(int j=maxp;j>=0;--j){
    			while(l<=r&&q[l]>j+bs) ++l;
    			while(l<=r&&f[i-w-1][q[r]]+q[r]*bp<=f[i-w-1][j]+j*bp) --r;
    			q[++r]=j;
    			if(l<=r) f[i][j]=max(f[i][j],f[i-w-1][q[l]]+(q[l]-j)*bp);
    		}
    	}
    	int ans=0;
    	for(int i=0;i<=maxp;++i) ans=max(ans,f[t][i]);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    40+个性鲜明的企业网站展示
    20+富有创意的BuddyPress网站
    TopFreeTheme精选免费模板【20130827】
    10本最新的Android开发电子书免费下载
    20个最棒的英文电子书免费下载网站
    合法免费下载电子书的站点整理收藏
    2013年19个最棒的HTML5网站模板免费下载
    [NUnit] discover test finished: 0 found issue
    完全数
    IP
  • 原文地址:https://www.cnblogs.com/autoint/p/10802530.html
Copyright © 2011-2022 走看看