题型:单调队列
题意:
Alice开了一个24小时营业的月饼店,从2000年1月1号0点开始营业,营业M个小时。
Alice只在整点时刻做月饼,做月饼不花时间。
每一个时刻做月饼的成本不一样,每个月饼的保质期为T,月饼的存储费用是每块月饼每小时花费S。
现在又N个订单,问完成这些订单需要花费的最少成本。
分析:
一开始用线段树来做,但是死活WA。
后来换了一个思路,发现只需要O(M)的复杂度单调队列就可以了。
先将订单转换成距离2000年1月1号0点的时间,注意闰年问题。
可以看出,对于一个订单的月饼,在某一个时刻全部做完是最好的。
订单的输入是按照时间先后排序的。如果前一个订单的收取时间是b,是在第a个小时完成的,那么后一笔订单一定是在在第a个小时完成或者是b小时之后完成,这样就满足了单调队列的性质了。
遍历M长的时间,不断查找完成最近一笔订单使得花费最少的时刻。需要注意制作月饼的时间范围,因为月饼有保质期。
最好所有订单需要的最少费用之和就是答案。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define LL __int64
using namespace std;
const int M = 100010;
const LL inf = 0x3f3f3f3f3f3f3f3fLL;
struct Date { //日期
int year, month, day;
};
char week[8][16]= {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday","Saturday"};
int days[12]= {31,28,31,30,31,30,31,31,30,31,30,31}; //日期函数
class DATE {
public:
int leap(int year) { //判闰年
return (year%4==0&&year%100!=0)||year%400==0;
}
int legal(Date a) { //判合法性
if(a.month<0||a.month>12) return 0;
if(a.month==2) return a.day>0 && a.day<=28+leap(a.year);
return a.day>0 && a.day<=days[a.month-1];
}
int datecmp(Date a, Date b) { //比较日期大小
if(a.year != b.year) return a.year - b.year;
if(a.month != b.month) return a.month - b.month;
return a.day - b.day;
}
int weekday(Date a) { //返回指定日期是星期几
int tm = a.month>=3 ? (a.month-2) : (a.month+10);
int ty = a.month>=3 ? a.year : (a.year-1);
return (ty+ty/4-ty/100+ty/400+(int)(2.6*tm-0.2)+a.day)%7;
}
int date2int(Date a) { //日期转天数偏移
int ret=a.year*365+(a.year-1)/4-(a.year-1)/100+(a.year-1)/400;
days[1]+=leap(a.year);
for(int i=0; i<a.month-1; ret+=days[i++]);
days[1]=28;
return ret+a.day;
}
Date int2date(int a) { //天数偏移转日期
Date ret;
ret.year = a/146097*400;
for(a%=146097; a>=365+leap(ret.year); a-=365+leap(ret.year),ret.year++);
days[1] += leap(ret.year);
for(ret.month=1; a>=days[ret.month-1]; a-=days[ret.month-1],ret.month++);
days[1]=28;
ret.day=a+1;
return ret;
}
} gx;
char yuefen[32][32] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
LL shi[M];
LL geshu[M];
LL cost[M];
LL a[M],b[M];
int main() {
int n,m;
char month[32];
Date pre,now;
pre.year = 2000;
pre.month = 1;
pre.day = 1;
while(1){
scanf("%d%d",&n,&m);
if(n == 0 && m == 0) return 0;
for(int i=1;i<=n;i++){
int day,year,H,R;
scanf("%s%d%d%d%d",month,&day,&year,&H,&R);
now.year = year;
now.day = day;
for(int j=0;j<12;j++){
if(!strcmp(month,yuefen[j])){
now.month = j+1;
break;
}
}
shi[i] = gx.date2int(now) - gx.date2int(pre);
shi[i] *= 24;
shi[i] += H+1;
geshu[i] = R;
}
LL T,S;
scanf("%I64d%I64d",&T,&S);
for(int i=1;i<=m;i++){
scanf("%I64d",&cost[i]);
}
int s = 1;
int t = 1;
LL ans = 0;
for(int i=1;i<=n;i++){
LL minn = inf;
int id = 0;
for(int j=t;j<=shi[i];j++){
if(shi[i]-j>T) continue;
LL now = (cost[j]+(shi[i]-j)*S)*geshu[i];
if(minn>now){
minn = now;
id = j;
}
}
if(shi[i]-s<=T){
LL now = (cost[s]+(shi[i]-s)*S)*geshu[i];
if(minn>now){
minn = now;
id = s;
}
}
ans += minn;
s = id;
t = shi[i];
}
printf("%I64d\n",ans);
}
return 0;
}