POJ 3040 Allowance

http://poj.org/problem?id=3040

这道题 没有思路欧

直接网上找到的证明

贪心,从大到小排序,只要不超额就能放多少就放多少,最后再从小的开始找一个放进去能超额的。

正确性证明,因为大的是小的倍数,所以大的放进去不超额一定要放进去,因为小的不管怎么取,
再超过c之前一定会凑成这个大的面额,那么用大的代替一定更优。

第一步做完之后,那么现在一定要再放进去一个硬币,那么选择最小的并且能大于c的也一定是最优的。

  1 #include <iostream>
  2 #include <stdio.h>
  3 #include <string.h>
  4 #include <fstream>
  5 #include <math.h>
  6 #include <algorithm>
  7 
  8 #define INF 111111111
  9 using namespace std;
 10 
 11 typedef long long ll;
 12 typedef pair<ll,ll> P;
 13 
 14 P p[28];
 15 ll cnt[28];
 16 ll n, c, week = 0;
 17 
 18 bool cmp(P p1, P p2)
 19 {
 20     return p1.first > p2.first;
 21 }
 22 
 23 ll getWeek()
 24 {
 25     ll min_num = 0x3f3f3f3f;
 26     for (int i = 0; i < n; i++)
 27     {
 28         if (cnt[i] != 0)
 29         {
 30             min_num = min(min_num, p[i].second / cnt[i]);
 31         }
 32     }
 33     for (int i = 0; i < n; i++)
 34     {
 35         if (cnt[i] != 0)
 36         {
 37             p[i].second -= min_num*cnt[i];
 38         }
 39     }
 40     return min_num;
 41 }
 42 
 43 int main()
 44 {
 45     ifstream cin("in.txt");
 46     freopen("in.txt","r", stdin);
 47     scanf("%d%d", &n, &c);
 48     for (int i = 0; i < n; i++)
 49     {
 50         scanf("%d%d", &p[i].first, &p[i].second);
 51     }
 52     sort(p, p+n, cmp);
 53     //不可节约型的发完
 54     for (int i = 0; i < n; i++)
 55     {
 56         if (p[i].first < c) break;
 57         week += p[i].second;
 58         p[i].second = 0;
 59     }
 60     //从大往小取 取到恰好小于 c
 61     while(1)
 62     {
 63         ll sum = c;
 64         memset(cnt, 0, sizeof(cnt));
 65         //从大开始取
 66         for (int i = 0; i < n; i++)
 67         {
 68             ll num = 0;
 69             if (p[i].second)
 70             {
 71                 num = min(p[i].second,sum / p[i].first);
 72                 sum -= num*p[i].first;
 73                 cnt[i] = num;
 74             }
 75         }
 76         if (sum > 0)
 77         {
 78             //从小开始取
 79             for (int i = n-1; i >= 0; i--)
 80             {
 81                 ll num;
 82                 if (p[i].second > cnt[i])//之前超时的Bug应该是 只判断p[i].second 而没有注意p[i].second其实一直没有变化 在最后算week时才改变
 83                 while(p[i].second > cnt[i])
 84                 {
 85                     sum -= p[i].first;
 86                     cnt[i]++;
 87                     if (sum <= 0) break;
 88                 }
 89                 if (sum <= 0)
 90                 {
 91                     break;
 92                 }
 93             }
 94         }
 95         if (sum > 0) break;
 96         week += getWeek();
 97         //更简洁的写法 代替get_week
 98 //        ll mx = INF;
 99 //        for (int i = n-1; i >= 0; i--)
100 //        {
101 //            if (cnt[i])
102 //            mx = min(mx, p[i].second / cnt[i]);// p[i].second / cnt[i]代表的就是可以给几个星期
103 //        }
104 //        for (int i = n-1; i >= 0; i--)
105 //        {
106 //            p[i].second -= mx*cnt[i];
107 //        }
108     }
109     printf("%lld\n",week);
110     return 0;
111 }

 

posted @ 2017-02-01 19:36  Lorazepam  阅读(270)  评论(0编辑  收藏  举报