题目在这里 http://acm.hdu.edu.cn/showproblem.php?pid=3401
很明显的动态规划,每一天都有三种情况,不买不卖,买或者卖
状态转移方程是:
对于买
f[i][j] = max (f[i][j], f[r][k] - (j - k) * ap[i]) 1 <= r <= i - w -1 , max(0, j - as[i]) <= k < j
对于卖
f[i][j] = max (f[i][j], f[r][k] + (j - k) * ap[i]) 1 <= r <= i - w -1 , min(maxp, j + bs[i]) >k > j
总的时间复杂度是0(maxp*maxp*T*T),一定要超时,要降低复杂度
优化思想:
首先对于每一天,枚举下j做一次
f[i][j] = max(f[i][j],f[i-1][j])
这样就可以保证对于f[i][j],f[i-w-1][]已经包含了前面的最好情况
不用再枚举上次交易是哪一天 1 <= k <= i - w -1这一维了
时间复杂度便成为0(maxp*maxp*T),不过还是不行
然后转移的时候:(只讨论买的情况)
f[i-1][r]-(j-r)*ap[i]
即 f[i-1][r]-j*ap[i]+r*ap[i]
外层循环 i,j 必要
则i j确定 ==> j*ap[i] 确定
最优的是f[i-1][r]+r*ap[i]最优
每次i,j循环都要计算但是我们可以把每个r对应的值算出来维护个单调队列
对于卖的情况是反序维护,具体见代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 /** 2 *Author : Zhou Hang 3 *Data : 2012-5-30 4 *Prob : trade 5 *Sol : dp with queue 6 */ 7 8 #include <cstdio> 9 #include <string> 10 #include <cstring> 11 #include <algorithm> 12 13 #define MaxN 2100 14 15 using namespace std; 16 17 struct node 18 { 19 int num, val; 20 }q[MaxN]; 21 22 int t,maxp,w; 23 int f[MaxN][MaxN]; 24 int ap[MaxN],bp[MaxN]; 25 int as[MaxN],bs[MaxN]; 26 27 28 /* 无优化 29 void Doit(int t,int maxp,int w) 30 { 31 //第i天结束后有j股票最大利润 32 memset(f,143,sizeof(f)); 33 f[0][0]=0; 34 for (int i=1; i<=t; i++) 35 for (int j=0; j<=maxp; j++) 36 { 37 //nothing to do 38 f[i][j]=f[i-1][j]; 39 40 //buy 41 // k'th day r ==>f[k][r] 42 for (int k=0; k<i-w; k++) 43 for (int r=0; r<=j-as[i]; r++) 44 { 45 int num=j-r; 46 f[i][j]=max( f[i][j],f[k][r]-num*ap[i] ); 47 } 48 49 //sell 50 // k'th day r ==>f[k][r] 51 for (int k=0; k<i-w; k++) 52 for (int r=j; r<=min(maxp,j+bs[i]); r++) 53 { 54 int num=r-j; 55 f[i][j]=max( f[i][j],f[k][r]+num*bp[i] ); 56 } 57 } 58 } 59 */ 60 61 62 void Doit(int t,int maxp,int w) 63 { 64 memset(f,143,sizeof(f)); 65 66 for(int i = 1; i <= w + 1; i++) 67 for(int j = 0; j <= min(as[i], maxp); j++) 68 f[i][j] = -ap[i] * j; 69 f[0][0]=0; 70 71 for (int i=2; i<=t; i++) 72 { 73 //nothing to do 74 for (int j=0; j<=maxp; j++) 75 f[i][j]=max(f[i][j],f[i-1][j]); 76 77 if (i<w+2) continue; 78 79 //buy 80 int head=1,tail=1; 81 q[1].num=0; q[1].val=f[i-w-1][0]; 82 83 for (int j=1; j<=maxp; j++) 84 { 85 //因为j递增 j - q[head].num > as[i] 之后的j也同样 86 while (head<=tail && j-q[head].num>as[i]) 87 head++; 88 if (head<=tail) f[i][j]=max(f[i][j], q[head].val-(j-q[head].num)*ap[i]); 89 while(tail>=head && q[tail].val+(q[tail].num-j)*ap[i]<f[i-w-1][j]) 90 tail--; 91 q[++tail].num = j; 92 q[tail].val = f[i- w - 1][j]; 93 } 94 95 //sell 96 head=tail=1; 97 q[1].num=maxp; q[1].val=f[i-w-1][maxp]; 98 for (int j = maxp - 1; j >= 0; j--) 99 { 100 while(tail >= head && q[head].num - j > bs[i]) 101 head++; 102 if(head<=tail) f[i][j]=max(f[i][j],q[head].val+(q[head].num-j)*bp[i]); 103 while(tail>=head && q[tail].val+(q[tail].num-j)*bp[i]<f[i - w - 1][j]) 104 tail--; 105 q[++tail].num = j; 106 q[tail].val = f[i - w - 1][j]; 107 } 108 } 109 110 int rmax=0; 111 for(int i = 0; i <= maxp; i++) 112 if(f[t][i] > rmax) rmax = f[t][i]; 113 printf("%d\n", rmax); 114 115 } 116 117 int main() 118 { 119 freopen("trade.in","r",stdin); 120 freopen("trade.out","w",stdout); 121 122 int T; 123 scanf("%d",&T); 124 //case number 125 for (int times=1; times<=T; times++) 126 { 127 scanf("%d%d%d",&t,&maxp,&w); 128 for (int i=1; i<=t; i++) 129 { 130 scanf("%d%d",&ap[i],&bp[i]); 131 scanf("%d%d",&as[i],&bs[i]); 132 } 133 Doit(t,maxp,w); 134 135 136 } 137 138 139 fclose(stdin); fclose(stdout); 140 return 0; 141 }
单调队列优化动归在我看来更多的就是避免重复计算用的,在思考上难度并不是很大,希望以后见到了能做出来吧