HDU_4122_Alice's mooncake shop(单调队列)

题型:单调队列


题意:

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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值