hdu 3401
地址:http://acm.hdu.edu.cn/showproblem.php?pid=3401
题意:炒股……规定每天最多买进多少,卖出多少,并且每次炒股后隔w天才能再次炒股,问最大收益。
mark:首先想到最朴素的dp。dp[i][j]代表第i天有j股会带来最大收益。
则dp[i][j] = max(dp[i-1][j], dp[r][k]-(j-k)ap[i], dp[r][k]+(k-j)bp[i]); 复杂度是O(T*T*Maxp*Maxp)。
首先想到第一步优化,前面式子r范围是0~i-w-1,怎么优化呢,因为dp[i][j]至少为dp[i-1][j],那么dp[i-w-1][k] >= dp[...][k]恒成立(...指r的范围内的数)
故dp[i][j] = max(dp[i-1][j], dp[i-w-1][k]-(j-k)ap[i], dp[i-w-1][k]+(k-j)bp[i]); 复杂度是O(T*Maxp*Maxp)。还是很大。
接下来就是单调队列的作用了,单调队列需要理解,就是维护一个两头出,一头进的队列,具体实现不好说。复杂度可以优化到O(T*Maxp)
代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> const int M = 2000+10; const int N = -10000000; int n,maxp,w; int dp[M][M]; int q1[M],q2[M]; int ap[M],bp[M],as[M],bs[M]; int max(int a, int b, int c) { int m = a > b ? a: b; return m > c ? m : c; } int min(int a, int b) {return a < b ? a : b;} void solve() { int max1,max2,min1,fr1,fr2,la1,la2; int i,j,k; for(i = 1; i <= w+1; i++) { min1 = min(as[i], maxp); for(j = 0; j <= min1; j++) dp[i][j] = -ap[i]*j; } for(i = 2; i <= n; i++) { for(j = 0; j <= maxp; j++) dp[i][j] = max(dp[i][j], dp[i-1][j], N); if(i <= w+1) continue; fr1 = la1 = 0; fr2 = la2 = 0; q2[la2++] = 0; min1 = min(bs[i], maxp); for(k = 1; k <= min1; k++) { while(dp[i-w-1][q2[la2-1]]+q2[la2-1]*bp[i] <= dp[i-w-1][k]+k*bp[i]) { la2--; if(fr2 == la2) break; } q2[la2++] = k; } for(j = 0; j <= maxp; j++) { if(j+bs[i] <= maxp) { while(dp[i-w-1][j+bs[i]]+(j+bs[i])*bp[i] >= dp[i-w-1][q2[la2-1]]+q2[la2-1]*bp[i]) { la2--; if(fr2 == la2) break; } q2[la2++] = j+bs[i]; } if(q2[fr2] == j) fr2++; if(fr2 == la2) max2 = N; else max2 = dp[i-w-1][q2[fr2]]+q2[fr2]*bp[i]-j*bp[i]; if(j) { if(fr1 == la1) q1[la1++] = j-1; else { while(dp[i-w-1][j-1]+(j-1)*ap[i] >= dp[i-w-1][q1[la1-1]]+q1[la1-1]*ap[i]) { la1--; if(fr1 == la1) break; } q1[la1++] = j-1; while(q1[la1-1]-q1[fr1] >= as[i]) fr1++; } max1 = dp[i-w-1][q1[fr1]]+q1[fr1]*ap[i]-j*ap[i]; } else max1 = N; dp[i][j] = max(dp[i][j], max1, max2); } } max1 = 0; for(i = 0; i <= maxp; i++) if(max1 < dp[n][i]) max1 = dp[n][i]; printf("%d\n", max1); } int main() { // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); int t; int i,j,k; scanf("%d", &t); while(t-- && scanf("%d%d%d", &n, &maxp, &w)) { for(i = 1; i <= n; i++) scanf("%d%d%d%d", ap+i, bp+i, as+i, bs+i); for(i = 0; i <= n; i++) for(j = 0; j <= maxp; j++) dp[i][j] = N; solve(); } return 0; }