BZOJ2424: [HAOI2010]订货

【传送门: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;
}

 

posted @ 2018-02-27 19:47  Star_Feel  阅读(164)  评论(0编辑  收藏  举报