zoukankan      html  css  js  c++  java
  • 【BZOJ2832&&3874】宅男小C [模拟退火][贪心]

    宅男小C

    Time Limit: 10 Sec  Memory Limit: 256 MB
    [Submit][Status][Discuss]

    Description

      众所周知,小C是个宅男,所以他的每天的食物要靠外卖来解决。小C现在有M元钱,他想知道这些钱他最多可以吃多少天。

      餐厅提供N种食物,每种食物有两个属性,单价Pi和保质期Si,表示小C需要花Pi元才能买到足够一天吃的这种食物,并且需要在送到Si天内吃完,否则食物会变质,就不能吃了,若Si为0则意味着必须在送到当天吃完。另外,每次送餐需要额外F元送餐费。

    Input

      每个测试点包含多组测试数据;
      每个测试数据第一行三个整数M,F,N,如题目描述中所述;
      以下N行,每行两个整数,分别表示PiSi

    Output

      对于每个测试数据输出一行,表示最多可以吃的天数。

    Sample Input

      32 5 2
      5 0
      10 2
      10 10 1
      10 10
      10 1 1
      1 5

    Sample Output

      3
      0
      8

    HINT

      对于40%的数据,M,Si <= 2*10^6;
      对于100%的数据,1 ≤ N ≤ 200,M, Si<= 10^18,1 ≤ T ≤ 50,1 ≤ F ≤ M,1 ≤ Pi ≤ M。

    Main idea

      每种食物有一个花费和一个保质期,在保质期内食用可以多活一天,每次购买可以买多个食物,买一次会耗费一些钱,问最多能活几天。

    Solution

      我们先从简单的做法入手,如果确定了购买次数,能求出最多活几天吗?答案是显然可以的。我们运用贪心:首先,若存在某种价格又贵保质期又短的食物显然是没有用的,我们sort一遍直接删去,然后我们可以得到一个价格上升且保质期上升的序列。我们基于这里开始贪心:我们先从便宜的食物入手,显然每次都是从这种食物吃起,仅存在两种不购买便宜的情况:1.保质期过了;2.钱不够满足所有次数了。如果保质期过了,我们就选择下一个食物,如果钱不够满足所有次数了,那就能买几次买几次,记录一下答案,退出。

      我们解决了确定购买次数最多活几天之后,再仔细思考:由于购买会花钱,那么我们大胆猜测购买次数和活的天数有一定的规律,我们画了几张图之后,发现其比例大致单峰,如下图所示:

      我们发现,显然中间有一段波动,那么就不能使用三分法了。那怎么办呢?但是我们再发现:函数最后波动段非常短!显然在随机范围内可行,那么显然我们可以使用随机化算法!这里我们运用模拟退火。直接模拟退火随机一个购买次数,然后Judge更新即可。

      随机化算法是坠吼的!(≧▽≦)/

    Code

      1 #include<iostream>  
      2 #include<algorithm>  
      3 #include<cstdio>  
      4 #include<cstring>  
      5 #include<cstdlib>  
      6 #include<cmath>  
      7 using namespace std;
      8 typedef long long s64;
      9    
     10 const int ONE = 10005;
     11 const int INF = 2147483640;
     12   
     13 int n;
     14 s64 A,Now,Ans;
     15 s64 Total,F;
     16   
     17 struct power
     18 {
     19         s64 cost;
     20         s64 days;
     21 }a[ONE];
     22   
     23 bool cmp(const power &a,const power &b)
     24 {
     25         if(a.cost == b.cost) return a.days > b.days;
     26         return a.cost < b.cost;
     27 }
     28   
     29 int get()
     30 {
     31         int res=1,Q=1;    char c;
     32         while( (c=getchar())<48 || c>57)
     33         if(c=='-')Q=-1;
     34         if(Q) res=c-48; 
     35         while((c=getchar())>=48 && c<=57) 
     36         res=res*10+c-48; 
     37         return res*Q; 
     38 }
     39   
     40 void pre()
     41 {
     42         sort(a+1,a+n+1,cmp);    s64 d=-1;
     43         int m=n;    n=0;
     44         for(int i=1;i<=m;i++)
     45         if(a[i].days > d)
     46             a[++n]=a[i], d=a[i].days;
     47 }
     48   
     49 s64 Judge(s64 times)
     50 {
     51         if(times<=0) return 0;
     52         s64 Money = Total - times * F;
     53         s64 res = 0, num, day = 0;
     54         for(int i=1;i<=n;i++)
     55         {
     56             num = min(Money / a[i].cost / times, a[i].days - day + 1);
     57             Money -= num * a[i].cost * times;
     58             day += num; res += times * num;
     59             if(day <= a[i].days)
     60             {
     61                 num = Money / a[i].cost;
     62                 res += num;
     63                 Ans = max(Ans, res);
     64                 return res;
     65             }
     66         }
     67         Ans = max(Ans, res);
     68         return res;
     69 }
     70   
     71 double Random() {return rand()/(double)RAND_MAX;}
     72 void SA(double T)
     73 {
     74         Now = 1;
     75         while(T >= 1)
     76         {
     77             A = Now + (s64)(T * (Random()*2-1)) ;
     78             if(A<=0) A = T*Random();
     79             s64 dE = Judge(A) - Judge(Now);
     80             if(dE > 0)
     81                 Now = A;
     82             T *= 0.93;
     83         }
     84          
     85 }
     86   
     87 void Solve()
     88 {
     89         for(int i=1;i<=n;i++)
     90             scanf("%lld %lld",&a[i].cost,&a[i].days);
     91         pre();
     92         Ans = 0;
     93         SA(Total / F + 1);
     94         printf("%lld
    ",Ans);
     95 }
     96  
     97 int main()
     98 {
     99         while(scanf("%lld %lld %d",&Total,&F,&n) != EOF)
    100             Solve();
    101 }
    View Code
  • 相关阅读:
    函数模板、函数模板特化、重载函数模板、非模板函数重载
    输出流格式化(以操纵子方式格式化,以ios类成员函数方式格式化)
    文件的读写、二进制文件的读写、文件随机读写
    文件流(fstream, ifstream, ofstream)的打开关闭、流状态
    流类库继承体系(IO流,文件流,串流)和 字符串流的基本操作
    对象语义与值语义、资源管理(RAII、资源所有权)、模拟实现auto_ptr<class>、实现Ptr_vector
    operator new 和 operator delete 实现一个简单内存泄漏跟踪器
    异常与继承、异常与指针、异常规格说明
    程序错误、异常(语法、抛出、捕获、传播)、栈展开
    C语言错误处理方法、C++异常处理方法(throw, try, catch)简介
  • 原文地址:https://www.cnblogs.com/BearChild/p/6542778.html
Copyright © 2011-2022 走看看