hdu4122 制作月饼完成订单的最小花费

题意:
      有一个加工厂加工月饼的,这个工厂一共开业m小时,2000年1月1日0点是开业的第一个小时,每个小时加工月饼的价钱也不一样,然后每个月饼的保质期都是t天,因为要放在冰箱里保存,所以在保质期期间每天每个月饼的花费是s,他接到了n个订单,问你完成这n个订单的最小花费是多少?


思路:
     想到这样一个性质,在一个序列里面某一个位置的值是由他前面的某一个范围中的一个得到的,而且我们要当前这个位置最小(或者最大),这样的问题可以用单调队列解决,这个题目我们可以弄一个递增的单调队列,小的那头要保证不过期,大的那头要保证递增,这样就ok了,还有对于这个题目,我的方法是吧所有的订单日期全都处理成小时(就是于题目中的m对应)去做,这样能清晰点,还有就是有两个小提示,订单当天做也来得及,还有一个就是两个订单的日期可能相同,别的没什么了,具体细节看下面代码。
      


#include<map>
#include<string>
#include<stdio.h>
#include<string.h>


#define N 2500 + 50


using namespace std;


map<__int64 ,__int64>mark;
map<string ,int>mk;
__int64 ry[13] = {0 ,31 ,29 ,31 ,30 ,31 ,30 ,31 ,31 ,30 ,31 ,30 ,31};
__int64 py[13] = {0 ,31 ,28 ,31 ,30 ,31 ,30 ,31 ,31 ,30 ,31 ,30 ,31};
__int64 C[110000] ,node[N];
__int64 hash[110000];
int Q[110000];
__int64 tou ,wei;


void DB()
{
   mk["Jan"] = 1; mk["Feb"] = 2; mk["Mar"] = 3;
   mk["Apr"] = 4; mk["May"] = 5; mk["Jun"] = 6;
   mk["Jul"] = 7; mk["Aug"] = 8; mk["Sep"] = 9;
   mk["Oct"] = 10; mk["Nov"] = 11; mk["Dec"] = 12;
}


bool jude(__int64 now)
{
   return now % 400 == 0 || now % 4 == 0 && now % 100 != 0;
}


void insert(int t ,__int64 tt,__int64 cost)
{
   for(int i = wei ;i > tou ;i --)
   {
      if(C[t] <= (__int64)(t - Q[wei]) * cost + C[Q[wei]])
      wei --;
      else break;
   }
   Q[++wei] = t;
   for(int i = tou + 1;i <= wei ;i ++)
   if(t - Q[i] > tt) tou ++;
}
   


int main ()
{
   int n ,m ,a ,i;
   __int64 y ,r ,h ,t ,cost;
   char mouse[10];
   DB();
   while(~scanf("%d %d" ,&n ,&m) && n + m)
   {
      mark.clear();
      for(i = 1 ;i <= n ;i ++)
      {
         scanf("%s %I64d %I64d %I64d %I64d" ,mouse ,&r ,&y ,&h ,&node[i]);
         __int64 now = mk[mouse] * 10000 + y * 1000000 + r * 100 + h * 1;      
         if(mark[now]) node[mark[now]] += node[i];
         else mark[now] = i;   
      }
      memset(hash ,0 ,sizeof(hash));
      __int64 nn = 2000,yy = 1 ,rr = 1,ss = 0;
      if(mark[nn*1000000+yy*10000+rr*100+ss*1])
      {
         hash[1] = mark[nn*1000000+yy*10000+rr*100+ss*1];
      }
      for(i = 2 ;i <= m ;i ++)
      {
         ss ++;
         if(ss == 24) {ss = 0 ,rr ++;}
         if(jude(nn) && rr == ry[yy] + 1 || !jude(nn) && rr == py[yy] + 1)
         {yy ++ ,rr = 1;}
         if(yy == 13) {yy = 1 ;nn ++;}
         if(mark[nn*1000000+yy*10000+rr*100+ss*1])
         hash[i] = mark[nn*1000000+yy*10000+rr*100+ss*1]; 
      }
      scanf("%I64d %I64d" ,&t ,&cost); 
      for(i = 1 ;i <= m ;i ++)
      scanf("%I64d" ,&C[i]);
      tou = wei = 0;
      __int64 Ans = 0;
      for(i = 1 ;i <= m ;i ++)
      {    
         insert(i ,t ,cost);
         if(hash[i])
         {  
            int T = Q[tou + 1];
            Ans +=  C[T] * node[hash[i]] + node[hash[i]] * (__int64)(i - T) * cost;
         }   
      }
      printf("%I64d\n" ,Ans);      
      
   }
   return 0;
}
      
         
         
         
         
      
   
   
   
   

















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值