hdu 3401 单调队列优化动态规划
思路:
动态方程很容易想到dp[i][j]=max(dp[i][j],dp[i-w-1][j-k]-k*ap[i],dp[i-w-1][j+k]+k*bp[i]);
dp[i][j]表示第i天拥有j个石头的最大价值。
其实每次求得都是最有策略,所有dp[i-w-1]表示的就是i-w-1以前的最优,故不同往前遍历。
那么主要需要优化的是:
对于买石头,容量为j时,维护从j-k到j的转移最大值。即从哪个容量转移过来能得到最大效益。
对于卖石头,容量为j时,维护从j+k到j的转移最大值。即从哪个容量转移过来能得到最大效益。
见代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define inf 10000010 #define Maxn 2010 #define Min(a,b) (a)>(b)?(b):(a) using namespace std; int dp[Maxn][Maxn],as[Maxn],bs[Maxn],ap[Maxn],bp[Maxn]; struct Que{ int val,pos; }que[10000]; int main() { int n,t,p,w,i,j,k; scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&p,&w); for(i=1;i<=n;i++) for(j=0;j<=p;j++) dp[i][j]=-inf;//初始化 for(i=1;i<=n;i++) scanf("%d%d%d%d",ap+i,bp+i,as+i,bs+i); for(i=1;i<=w+1;i++)//预处理 for(j=0;j<=as[i];j++) dp[i][j]=-j*ap[i]; for(i=2;i<=n;i++) { for(j=0;j<=p;j++) dp[i][j]=max(dp[i][j],dp[i-1][j]);//预处理 if(i<=w+1) continue; int head=1,rear=0; for(j=0;j<=p;j++)//买石头 { while(head<=rear&&(que[rear].val-(j-que[rear].pos)*ap[i])<=dp[i-w-1][j]) rear--; que[++rear].pos=j,que[rear].val=dp[i-w-1][j]; if(que[head].pos<j-as[i]) head++; dp[i][j]=max(dp[i][j],que[head].val+(que[head].pos-j)*ap[i]); } head=1,rear=0; for(j=p;j>=0;j--)//卖石头 { while(head<=rear&&(que[rear].val+(que[rear].pos-j)*bp[i])<=dp[i-w-1][j]) rear--; que[++rear].pos=j,que[rear].val=dp[i-w-1][j]; if(que[head].pos>j+bs[i]) head++; dp[i][j]=max(dp[i][j],que[head].val+(que[head].pos-j)*bp[i]); } } int Max=0; for(i=0;i<=p;i++)//找出最大效益 if(dp[n][i]>Max) Max=dp[n][i]; printf("%d\n",Max); } return 0; }