题目大意:
Alice经营了一家月饼店,有一系列的订单,对于每个订单,Alice可以立即去做月饼,或者用之前做好的月饼交货。Alice做月饼是不需要时间的,订单只会在每个小时的整点到达。每个小时做月饼的成本都不同,但是Alice可以预先做好月饼把它们放在冰箱里,每个月饼每小时的冷藏费用是S,最多能保存T个小时,求完成所有订单的最小成本。
算法实现:
这道题目最重要的是求出每个小时做月饼的最小成本(即现做和冷藏两种途径中的最小成本)。我们可以用一个优先队列去维护它们,优先队列记录的是每个小时的制作成本。每经过一个小时,优先队列中的每个元素的权值都应该增加S,然后再把当前这个小时的制作成本加到队列中,但是我们是不可能把优先队列中的每个元素的权值都加上S的。为了保持这种优先级关系,我们把当前这个小时的制作成本减去S再加入优先队列中,优先队列中的原有元素的权值保持不变。同时优先队列中的每个元素也要包含它入队时间,以后用来判断是否过期。对于每个小时,我们取优先队列中权值最小的元素,如果它过期了,抛弃再取;如果没有过期,则它是这个小时制作月饼的最小成本(因为入队之前减过若干个S,出队时也要复原)。这道题目最多有10W小时,每个小时入队一遍、出队最多一遍;总的查询次数成功N遍、失败最多N遍,因此时间复杂度是O(NlogN)。网上的解法都是单调队列、RMQ、线段树,但是我觉得我的方法也没错,仅供参考。
参考代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define MAXN 2510
#define MAXM 100010
#define LL long long
using namespace std;
struct Time
{
int index,cost;
bool operator > (Time other) const
{
return cost>other.cost;
}
};
priority_queue<Time,vector<Time>,greater<Time> > pq;
int order[MAXN],cnt[MAXN],minc[MAXM];
int calc(int y,char *str,int d,int h)
{
char name[13][10]={"","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
int m,ret=0,a[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
for(int i=1;i<=12;i++)
if(!strcmp(str,name[i]))
{
m=i;
break;
}
for(int i=2000;i<y;i++)
if(i%400==0||i%4==0&&i%100!=0)
ret+=366*24;
else
ret+=365*24;
if(y%400==0||y%4==0&&y%100!=0)
a[2]=29;
for(int i=1;i<m;i++)
ret+=a[i]*24;
ret+=(d-1)*24;
ret+=h+1;
return ret;
}
int main()
{
int N,M;
while(~scanf("%d%d",&N,&M))
{
if(N==0&&M==0)
break;
for(int i=1;i<=N;i++)
{
char m[10];
int d,y,h,R;
scanf("%s%d%d%d%d",m,&d,&y,&h,&R);
int tmp=calc(y,m,d,h);
order[i]=tmp;
cnt[i]=R;
}
int T,S;
scanf("%d%d",&T,&S);
while(!pq.empty())
pq.pop();
for(int i=1;i<=M;i++)
{
Time time;
scanf("%d",&time.cost);
time.cost-=S*i;
time.index=i;
pq.push(time);
while(1)
{
Time cur=pq.top();
if(i-cur.index<=T)
{
minc[i]=cur.cost+S*i;//minc[i]=cur.cost+S*cur.index+S*(i-cur.index)
break;
}
pq.pop();
}
}
LL ans=0;
for(int i=1;i<=N;i++)
if(order[i]<=M)
ans+=cnt[i]*minc[order[i]];
printf("%lld\n",ans);
}
return 0;
}