题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3401
题意:告诉每天买卖股票的价格,求获得的最大钱。
限制:1.每天买卖的数量限制。AS,BS
2.股票拥有总量的限制。Maxp
3.相邻两次买卖股票的天数限制。第i天交易之后,必须等到i+w+1天才能开始交易。
这题的动态规划方程:
1.不买不卖。dp[i][j]=max(dp[i-1][j],dp[i][j]);
2.买一些股票。dp[i][j]=max(dp[i-1][k]-(j-k)*ap[i]);(0<=k<=j<=Maxp)
3.卖一些股票。dp[i][j]=max(dp[i-1][k]+(k-j)*bp[i]);(0<=j<=k<=Maxp)
注意:
初始化问题:
1.把所有未定义的状态都设置为-INF,1~w+1天得初始化,因为这些天不能有前面的状态推出来,初始化为对应购买多少股票所减去的钱数,因为一开始只有买,不能卖。
2.对于每天买卖的数量限制,单调队列要加一个变量来控制是否能从一个状态到另一个状态,而且还是最大的值。
3.循环的递减还是递增问题,跟当前要访问的状态有关。
代码:
#include<iostream> #include<cstdio> #include<cstring> #define maxn 2005 #define INF 0xfffffff #define min(a,b) a<b?a:b #define max(a,b) a>b?a:b using namespace std; int n,Mp,w; int ap[maxn],bp[maxn],as[maxn],bs[maxn]; struct node { int pos; int f; }; node q[maxn]; int dp[maxn][maxn]; void init() { for(int i=0; i<=n; i++) { for(int j=0; j<=Mp; j++) { dp[i][j]=-INF; //求最大值初始状态都是-INF } } for(int i=1; i<=w+1; i++) //初始化1~w+1天,这些天只能买,不能卖 { for(int j=0; j<=as[i]; j++) { dp[i][j]=-ap[i]*j; } } dp[0][0]=0; } int main() { int cas,i,j,nowf,head,tail; scanf("%d",&cas); while(cas--) { scanf("%d%d%d",&n,&Mp,&w); for(i=1; i<=n; i++) { scanf("%d%d%d%d",&ap[i],&bp[i],&as[i],&bs[i]); } init(); for(i=1; i<=n; i++) { for(j=0; j<=Mp; j++) //不买不卖 { dp[i][j]=max(dp[i-1][j],dp[i][j]); } if(i-w-1>=1) { head=tail=0; for(j=0; j<=Mp; j++) //买一些股票,已记录0~j-1 { nowf=dp[i-w-1][j]+j*ap[i]; while(head<tail&&q[tail-1].f<nowf)//注意tail指向下一个位置,找nowf大的第一个数 tail--; q[tail].f=nowf; q[tail++].pos=j; while(head<tail&&q[head].pos+as[i]<j)//pos记录数量,数量限制,取数量能够到达j的最大值 head++; dp[i][j]=max(dp[i][j],q[head].f-j*ap[i]); } head=tail=0; for(j=Mp; j>=0; j--) //逆序,卖一些股票,已记录j+1~Mp { nowf=dp[i-w-1][j]+j*bp[i]; while(head<tail&&q[tail-1].f<nowf) tail--; q[tail].f=nowf,q[tail++].pos=j; while(head<tail&&q[head].pos-bs[i]>j) head++; dp[i][j]=max(dp[i][j],q[head].f-j*bp[i]); } } } int ans=0; for(i=0; i<=Mp; i++) ans=max(dp[n][i],ans); printf("%d\n",ans); } return 0; }