洛谷 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能赚到的最多的钱数。
输入输出样例
5 2 0 2 1 1 1 2 1 1 1 3 2 1 1 4 3 1 1 5 4 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; }
话说,为什么大家的题解几乎都是一样的。。(我也是看别人的。。。)