http://acm.hdu.edu.cn/showproblem.php?pid=3401
【题意】
- 知道之后n天的股票买卖价格(api,bpi),以及每天股票买卖数量上限(asi,bsi),问他最多能赚多少钱。开始时有无限本金,要求任两次交易需要间隔W天以上,即第i天交易,第i+w+1天才能再交易。同时他任意时刻最多只能拥有maxp的股票
【思路】
- dp[i][j]表示第i天拥有j支股票的最大收益,有三种转移方案:
- dp[i][j]=max(dp[i][j],dp[i-1][j])表示第i天不买也不卖,由前一天转移而来
- dp[i][j]=max(dp[i][j],dp[i-w-1][k]-(j-k)*ap[i])表示第i天买股票,有第i-w-1天转移而来
- dp[i][j]=max(dp[i][j],dp[i-w-1][k]+(k-j)*bp[i])表示第i天卖股票,有第i-w-1天转移而来
- 注意只需计算由i-w-1天转移而来,因为i-w-1天前的最优值已经通过不买不卖转移到了i-w-1天,即dp[i][j],j固定是随i单调递增的
- 现在dp的复杂度是n^3,怎样降低复杂度?
- 分析买股票的情况,dp[i][j]=max(dp[i-w-1][k]+k*ap[i])-j*ap[j],类似a[i]=max(b[k])+c[i],可以用单调队列优化
- 我理解的是,状态数为2D,转移为1D,然后又有单调性,可以固定一维状态,把转移均摊到另一维,相当于转移是O(1)的,所以单调队列可以把dp降一维
- a[i]=max(b[k]),若k<=j是从前往后递推,若k>=j是从后往前递推
【AC】
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=2e3+5; 5 const int inf=0x3f3f3f3f; 6 int ap[maxn],bp[maxn],as[maxn],bs[maxn]; 7 int n,maxp,w; 8 int dp[maxn][maxn]; 9 struct node 10 { 11 int x; 12 int num; 13 }q[maxn]; 14 int main() 15 { 16 int T; 17 scanf("%d",&T); 18 while(T--) 19 { 20 memset(dp,-inf,sizeof(dp));//求最大值,所以初始化为无穷小 21 scanf("%d%d%d",&n,&maxp,&w); 22 for(int i=1;i<=n;i++) scanf("%d%d%d%d",&ap[i],&bp[i],&as[i],&bs[i]); 23 for(int i=1;i<=n;i++) dp[i][0]=0;//拥有股票为0的最大收益当前是0 24 //前w+1天和[w+2,n]要分开算 25 //前w+1天只有两种情况:1.每天都不买不卖 2.其中一天买了股票 不能卖股票,而且最多只有一天能交易 26 for(int i=1;i<=w+1;i++) 27 { 28 for(int j=1;j<=as[i];j++) 29 { 30 dp[i][j]=-j*ap[i];//可以选择在当天买 31 } 32 } 33 for(int j=1;j<=maxp;j++) 34 { 35 for(int i=2;i<=w+1;i++) 36 { 37 dp[i][j]=max(dp[i][j],dp[i-1][j]);//也可以选择由前一天转移而来 38 } 39 } 40 for(int i=w+2;i<=n;i++) 41 { 42 int head=1,tail=0; 43 for(int j=0;j<=maxp;j++) 44 { 45 dp[i][j]=max(dp[i][j],dp[i-1][j]);//不买也不卖 46 //dp[i][j]=max(dp[i-w-1][k]+k*ap[i])-j*ap[i],其中k<=j 47 while(head<=tail&&q[tail].x<=dp[i-w-1][j]+j*ap[i]) tail--; 48 q[++tail].x=dp[i-w-1][j]+j*ap[i];q[tail].num=j; 49 while(q[head].num+as[i]<j) head++; 50 dp[i][j]=max(dp[i][j],q[head].x-j*ap[i]); 51 } 52 //dp[i][j]=max(dp[i-w-1][k]+k*bp[i])-j*bp[i],其中k>=j 53 head=1,tail=0; 54 for(int j=maxp;j>=0;j--) 55 { 56 while(head<=tail&&q[tail].x<=dp[i-w-1][j]+j*bp[i]) tail--; 57 q[++tail].x=dp[i-w-1][j]+j*bp[i];q[tail].num=j; 58 while(q[head].num>bs[i]+j) head++; 59 dp[i][j]=max(dp[i][j],q[head].x-j*bp[i]); 60 } 61 } 62 int ans=0; 63 for(int i=0;i<=maxp;i++) 64 { 65 ans=max(ans,dp[n][i]); 66 } 67 printf("%d\n",ans); 68 } 69 70 return 0; 71 }