题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4122
http://poj.org/problem?id=4002
题意:一个生产月饼的工厂,给出一个数m,该工厂只在前m小时(也就是[1,m])生产月饼。给出一系列订单,订单给出在第i小时买家要拿走R数量的月饼(1<=i<=m)。生产一个月饼的单价每天不同。工厂有一个冰箱,可以将提前生产的月饼放在冰箱里(工厂也可以在订单到来的那个时刻生产,生产月饼可以瞬间完成),但是放在冰箱里的时间不能超过T。每个月饼在冰箱里保存一天需要额外的费用S。制定工厂的生产计划使得总花费最少?冰箱无限大,每小时生产月饼数量没有限制。
思路:对于每个订单,设其到来时间为x,其实我们只要知道从x向前长度为T的区间之内,即[x-T,x],最小代价的是多少即可。另外,对于某个时间x1以及之前的一个时间x0,若p[x0]+(x1-x0)*S>=p[x1](p[i]表示时间i生产一个月饼的花费),则x0这个记录就不再需要记录,因为以后至少x1会比x0好。基于这样的事实,可以使用单调队列。
struct node
{
char s[10];
int day,year,H,R;
void get()
{
scanf("%s%d%d%d%d",s,&day,&year,&H,&R);
}
};
const int MAX=100005;
node b[2505];
int n,m,S,T,p[MAX],t[2505];
char map[12][10]={"Jan","Feb","Mar","Apr",
"May","Jun","Jul","Aug",
"Sep","Oct","Nov","Dec"};
int M[2][13]={
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
};
int Y[]={365,366};
int getMonth(char s[])
{
int i;
for(i=0;i<12;i++) if(strcmp(map[i],s)==0) break;
return i+1;
}
int OK(int x)
{
return x%400==0||x%100&&x%4==0;
}
int cal(node a)
{
int year=a.year;
int month=getMonth(a.s);
int day=a.day;
int ans=0,t,i;
for(i=2000;i<year;i++) ans+=Y[OK(i)];
t=OK(year);
for(i=1;i<month;i++) ans+=M[t][i];
ans+=day-1;
return ans*24+a.H+1;
}
struct Node
{
int x,y;
};
Node Q[MAX];
int head,tail;
int main()
{
while(scanf("%d%d",&n,&m),n||m)
{
int i;
for(i=1;i<=n;i++) b[i].get(),t[i]=cal(b[i]);
scanf("%d%d",&T,&S);
for(i=1;i<=m;i++) scanf("%d",&p[i]);
__int64 ans=0;
int j=1;
head=tail=0;
for(i=1;i<=m;i++)
{
while(head<tail&&Q[tail-1].x+(i-Q[tail-1].y)*S>=p[i])
tail--;
Q[tail].x=p[i];
Q[tail].y=i;
tail++;
while(j<=n&&t[j]==i)
{
while(head<tail&&Q[head].y+T<i) head++;
ans+=((__int64)(Q[head].x+(i-Q[head].y)*S))*b[j].R;
j++;
}
}
printf("%I64d\n",ans);
}
return 0;
}