1 5 2 0 2 1 1 1 2 1 1 1 3 2 1 1 4 3 1 1 5 4 1 1
3
上午做了两个单调队列简直太机智2333
这个题是说有一神人能预测股票走向,每天已知买进一只股票花的钱ap、抛一只股票挣的钱bp、每天买的数量最大值as、每天卖股票的最大值bs,而且要求任何操作之间都得隔w天,手头的股票最多m股,问你t天之后最多能挣多少钱。就是开始的时候资本是0,欠钱买股票,问最后手里多少钱,你这么神你怎么不自己算→_→
很容易想到转移方程式:对比前一次买进股票dp[i][j]=dp[i-w-1][k]-(j-k)*ap 抛出股票dp[i][j]=dp[i-w-1][k]+(j-k)*bp dp数组表示i天,手头有j支股票时赚的钱,单看这个方程时间复杂度达到三次方,用单调队列优化能降一维,具体操作呢?其实我没懂为啥要这么替换,应该是套路吧。对于抛出股票的行为,设tmp=dp[k][j]+bp*j,维护v[]数组作为单调队列,。j从m遍历到0,每次计算tmp插入单调队列中,这个操作说起来很纠结,也可能是我想错了,欢迎指正,插入队列时j的意义在于将上一次的值插入,然后取队首元素作为最大值,等到dp的递推就是求得当前的最大值,然后因为是卖出去,所以遍历是从大到小,我们设的v[]与实际的dp[]数组相差-bp*j,我们维护单调队列的意义在于每次及时找到当前的最大值嘛,递推dp的时候把差的加上就好啦。还有就是控制一次性抛出的最大值:这个时候j的意义就变成是当前值了,当前的股票数量-队首元素的股票数量若大于bs,一直出队列。应该是说明白了QAQ
/******************* hdu3401 2016.2.23 202MS 17436K 1621 B C++ *******************/ #include <iostream> #include<cstdio> #include<cstring> using namespace std; int t,n,maxp,w,ap,bp,as,bs,dp[2004][2004],v[2004],q[2004],l,r; int max(int a,int b) {return a>b?a:b;} int main() { // freopen("cin.txt","r",stdin); scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&maxp,&w); for(int i=1;i<=w+1;i++) { scanf("%d%d%d%d",&ap,&bp,&as,&bs); for(int j=0;j<=maxp;j++) { if(j<=as)dp[i][j]=-j*ap; else dp[i][j]=-1e9; if(i>1) dp[i][j]=max(dp[i-1][j],dp[i][j]); } } for(int i=w+2;i<=n;i++) { scanf("%d%d%d%d",&ap,&bp,&as,&bs); int k=i-w-1; l=0,r=-1;//buy for(int j=0;j<=maxp;j++) { int tmp=dp[k][j]-ap*(maxp-j);//dp下标咋还能写错 while(l<=r&&v[r]<tmp) r--; q[++r]=j; v[r]=tmp; while(j-q[l]>as) l++;//除去的是差值不满足条件的,天数条件已经控制 dp[i][j]=max(dp[i-1][j],v[l]+ap*(maxp-j)); } //sell l=0,r=-1; for(int j=maxp;j>=0;j--) { int tmp=dp[k][j]+bp*j; while(l<=r&&v[r]<tmp) r--; q[++r]=j; v[r]=tmp; while(q[l]-j>bs) l++; dp[i][j]=max(dp[i][j],v[l]-bp*j); } } printf("%d\n",dp[n][0]); } return 0; }