【传送门:BZOJ2424】
简要题意:
给出n天所需要的货物和每一天的货物的单价,可以多买一些货物存放在容量为s的仓库里,每一个货物存放一天需要m的花费,求出满足n天所需的货物的最小花费
题解:
先写了个DP方程:f[i][j]=min(f[i][j],f[i-1][k]+d[i]*(j+u[i]-k)+m*k);(0<=j<=s,0<=k<=j+u[i])
O(n*s^2),T的结果很明显
优化一下,很容易发现其实f数组是具有一定的单调性的
那么用单调队列来优化一下下
当f[i-1][list[head]]+d[i]*(j+u[i]-list[head])+m*list[head]>f[i-1][list[head+1]]+d[i]*(j+u[i]-list[head+1])+m*list[head+1]的时候说明list[head+1]对于当前情况更优,所以head++,不过要判断一下list[head+1]是否<=j+u[i]
其实f[i-1][list[head]]+d[i]*(j+u[i]-list[head])+m*list[head]>f[i-1][list[head+1]]+d[i]*(j+u[i]-list[head+1])+m*list[head+1]
可以转化为f[i-1][list[head]]-(d[i]-m)*list[head]>f[i-1][list[head+1]]-(d[i]-m)*list[head+1],将其中不变的值去掉
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int u[51],d[51]; int f[51][11000]; int list[11000]; int main() { int n,m,s; scanf("%d%d%d",&n,&m,&s); for(int i=1;i<=n;i++) scanf("%d",&u[i]); for(int i=1;i<=n;i++) scanf("%d",&d[i]); memset(f,63,sizeof(f)); f[0][0]=0; for(int i=1;i<=n;i++) { int head=1,tail=s+1; for(int j=0;j<=s;j++) list[j+1]=j; for(int j=0;j<=s;j++) { while(head<=tail&&f[i-1][list[head]]-(d[i]-m)*list[head]>f[i-1][list[head+1]]-(d[i]-m)*list[head+1]&&list[head+1]<=j+u[i]) head++; int k=list[head]; f[i][j]=f[i-1][k]+d[i]*(j+u[i]-k)+m*k; } } printf("%d\n",f[n][0]); return 0; }