您的位置:首页 > 其它

hdu 3401(单调队列优化dp)

2017-10-15 17:36 337 查看
传送门

题解:

构造状态dp[i][j]表示第i 天拥有 j只股票的时候,赚了多少钱

从前一天不买不卖 dp[i][j]=max(dp[i-1][j],dp[i][j])

从前i-W-1天买进一些股 dp[i][j]=max(dp[i-W-1][k]-(j-k)*AP[i],dp[i][j])

从i-W-1天卖掉一些股 dp[i][j]=max(dp[i-W-1][k]+(k-j)*BP[i],dp[i][j])

为什么只考虑第i-W-1天的买入卖出情况即可。想想看,i-W-2天是不是可以通过不买不卖将自己的最优状态转移到第i-W-1天?以此类推,之前的都不需要考虑了,只考虑都i-W-1天的情况即可。

对买入股票的情况进行分析,转化成适合单调队列优化的方程形式

dp[i][j]=max(dp[i-W-1][k]+k*AP[i])-j*AP[i]。令f[i-W-1][k]=dp[i-W-1][k]+k*AP[i],则dp[i][j]=max(f[i-W-1][k]) - j*AP[i]。

P.S.1~w+1天不能更新。还有,dp转移时一定要加一句if,否则会出现不合法转移(比如卖股票时q[t] - j < 0)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=2004;
int n,m,w,dp[MAXN][MAXN];
int ap[MAXN],as[MAXN],bp[MAXN],bs[MAXN];
int q[MAXN],h,t;
inline int read() {
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
int main() {
//  freopen("hdu 3401.in","r",stdin);
int kase=read();
while (kase--) {
n=read(),m=read(),w=read();
for (int i=1;i<=n;++i) ap[i]=read(),bp[i]=read(),as[i]=read(),bs[i]=read();
memset(dp,-0x3f,sizeof(dp));
dp[0][0]=0;
for (int i=1;i<=w+1;++i)
for (int j=0;j<=min(as[i],m);++j)
dp[i][j]=-ap[i]*j;
for (int i=1;i<=n;++i) {
for (int j=0;j<=m;++j)
dp[i][j]=max(dp[i-1][j],dp[i][j]);
if (i<=w+1) continue;
int pre=i-w-1;
h=1,t=0;
for (int j=0;j<=m;++j) {
while (h<=t&&q[h]+as[i]<j) ++h;
if (q[h]<=j) dp[i][j]=max(dp[i][j],dp[pre][q[h]]-ap[i]*(j-q[h]));
while (h<=t&&dp[pre][q[t]]-ap[i]*(j-q[t])<dp[pre][j]) --t;
q[++t]=j;
}
h=1,t=0;
for (int j=m;~j;--j) {
while (h<=t&&q[h]-bs[i]>j) ++h;
if (q[h]>=j) dp[i][j]=max(dp[i][j],dp[pre][q[h]]+bp[i]*(q[h]-j));
while (h<=t&&dp[pre][q[t]]+bp[i]*(q[t]-j)<dp[pre][j]) --t;
q[++t]=j;
}
}
int ans=0;
for (int j=0;j<=m;++j)
ans=max(ans,dp
[j]);
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  单调队列优化dp