http://acm.hdu.edu.cn/showproblem.php?pid=3401
做了一天的一道题目,一开始想复杂了,或者本来就没有搞清楚 。。。
题意:
给你T天股票的信息,每天的信息分别可以表示为:APi , BPi , ASi , BSi,分别表示第
i天时股票的买入单价、卖出单价、第i天最多可以买入的股票数、最多可以卖出的股票
数,并且还有一个约束条件,那就是两个交易日相距的天数要大于W天,问最终最多能
获利多少。
思路:
这题的dp方程很容易列,用dp[i][j] 表示第i天持有j股的最大拥有的钱数,转移方程为:
不买不卖:
dp[i][j] = MAX( dp[i][j] , dp[i-1][j] ) ;
买入:
dp[i][j] = MAX( dp[ pre ][ k] - (j - k) * AP[i] );
则:dp[i][j] + j * AP[i] = MAX( dp[pre][k] + k *AP[i] );
令FF(k) = dp[ pre ][k] + k * AP[i] ;
则:dp[i][j] = MAX( FF(k) ) - j *AP[i] ; //这样就转化为经典的单调dp了,我们只需要用一个
单调队列来存放k的决策点就可以了。
卖出:
dp[i][j] = MAX( dp[pre][k] + (k - j )*BP[i] );
则:dp[i][j] + j * AP[i] = MAX( dp[pre][k] + k *BP[i] );
令FFF(k) = dp[ pre ][k] + k * BP[i] ;
则:dp[i][j] = MAX( FFF(k) ) - j *AP[i] ; //情况和上面的类似。
这样每次求dp[i][j] 的时候,我们可以得出以下的一个重要结论:在第i天的时候,最优的pre
应该是:pre = i - W 。其实证明这个结论很简单, 假设pre1 < pre2,我们假设dp[pre1][k] >
dp[pre2][k] ,但是这种情况是不可能出现的,因为我们可以这样考虑,我现在从pre1天到
pre2天这中间都不交易,那么我就可以得到dp[pre2][k] >= dp[pre1][k],所以说上面的假设是
不可能成立的,因此我们每次只要将pre赋值为i - W就可以了(一开始我还在这里加了一个
单调队列维护,呃。。。)。接着就是每次求dp[i][j] 的时候分别用两个队列分别维护买入和
卖出就可以了。
代码:
#include<stdio.h>
#include<string.h>
#define MAX(a,b) (a)>(b)?(a):(b)
const int inf = (1<<30) ;
int TT ;
const int MAXN = 2010 ;
int T , MAXP , W ;
int AP[MAXN] , BP[MAXN] , AS[MAXN] , BS[MAXN] ;
int dp[MAXN][MAXN] ;
int que1[MAXN] ,que2[MAXN] ;
int f1, r1 , f2, r2 ;
void DP(){
for(int i=0;i<=T;i++){
for(int j=0;j<=MAXP;j++){
dp[i][j] = -inf ;
}
}
dp[0][0] = 0 ;
int pre = 0 ;
for(int i=1;i<=T;i++){
//不买不卖
for(int j=0;j<=MAXP;j++){
dp[i][j] = MAX( dp[i][j] , dp[i-1][j] );
}
//买
f1 = r1 = 0 ;
que1[r1++] = 0 ;
for(int j=1;j<=MAXP;j++){
while( f1 < r1 ){
if( j-que1[f1] <= AS[i] ) break ;
else f1++ ;
}
if(f1 == r1) break ;
int a = que1[f1] ;
dp[i][j] = MAX( dp[i][j] , dp[pre][a] - (j-a)*AP[i] );
int aa = dp[pre][j] + j * AP[i] ;
while(f1 < r1){
int b = que1[r1-1] ;
int bb = dp[pre][b] + b * AP[i] ;
if(aa > bb) r1-- ;
else break ;
}
que1[r1++] = j ;
}
//卖
f2 = r2 = 0 ;
que2[r2++] = MAXP ;
for(int j=MAXP-1;j>=0;j--){
while(f2 < r2){
if( que2[f2]-j <= BS[i] ) break ;
else f2++;
}
if(f2 == r2) break ;
int b = que2[f2] ;
dp[i][j] = MAX( dp[i][j] , dp[pre][b] + (b-j)*BP[i] );
int aa = dp[pre][j]+j*BP[i] ;
while(f2 < r2){
int b = que2[r2-1] ;
int bb = dp[pre][b] + b*BP[i] ;
if(aa > bb) r2-- ;
else break ;
}
que2[r2++] = j;
}
if( pre < i - W ){
pre = i - W ;
}
}
int _max = -inf ;
for(int j=0;j<=MAXP;j++){
_max = MAX( _max, dp[T][j] );
}
printf("%d\n",_max);
}
int main(){
scanf("%d",&TT);
for(int cas=1;cas<=TT;cas++){
scanf("%d%d%d",&T,&MAXP,&W);
for(int i=1;i<=T;i++)
scanf("%d%d%d%d",&AP[i],&BP[i],&AS[i],&BS[i]);
DP();
}
return 0;
}