题目链接: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;
}