poj-3040 Allowance (贪心)

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

FJ 有n种不同面值的硬币,每种硬币都有相应的个数,大面值的硬币值总能被小面值的硬币值整除,每周需要支付 Bessie   c元,问最多能支付Bessie多少周。

这题之所以能贪心,据说关键是这句话 where each denomination of coin evenly divides the next-larger denomination。

如果没有这个限制条件,有些情况是取不到最优解的。

把面值从小到大排序,然后从尾到头开始扫,能给多少就给多少,如果面值不够,就从头到尾扫,尽量取刚好超过数额的。

参考了别人代码,感觉写的很精妙。自己的水平还很欠缺。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cmath>
 4 #include <vector>
 5 #include <cstring>
 6 #include <string>
 7 #include <algorithm>
 8 #include <string>
 9 #include <set>
10 #include <functional>
11 #include <numeric>
12 #include <sstream>
13 #include <stack>
14 #include <map>
15 #include <queue>
16 
17 #define CL(arr, val)    memset(arr, val, sizeof(arr))
18 
19 #define ll long long
20 #define inf 0x7f7f7f7f
21 #define lc l,m,rt<<1
22 #define rc m + 1,r,rt<<1|1
23 #define pi acos(-1.0)
24 
25 #define L(x)    (x) << 1
26 #define R(x)    (x) << 1 | 1
27 #define MID(l, r)   (l + r) >> 1
28 #define Min(x, y)   (x) < (y) ? (x) : (y)
29 #define Max(x, y)   (x) < (y) ? (y) : (x)
30 #define E(x)        (1 << (x))
31 #define iabs(x)     (x) < 0 ? -(x) : (x)
32 #define OUT(x)  printf("%I64d\n", x)
33 #define lowbit(x)   (x)&(-x)
34 #define Read()  freopen("a.txt", "r", stdin)
35 #define Write() freopen("b.txt", "w", stdout);
36 #define maxn 1000000000
37 #define N 100010
38 using namespace std;
39 
40 pair<int,int>p[25];
41 int use[25];
42 int main()
43 {
44     int n,c,sum;
45     scanf("%d%d",&n,&c);
46     sum=0;
47     for(int i=0;i<n;i++)   scanf("%d%d",&p[i].first,&p[i].second);
48     sort(p,p+n);
49     for(int i=0;i<n;i++)  //从大面值  开始 如果面值 大于c 那么直接累加。
50         if(p[i].first>=c)
51         {
52             sum+=p[i].second;
53             p[i].second=0;
54         }
55     while(true)  //不断循环,直到不能在支付为止。
56     {
57         int tmp=c;
58         int flag=0;
59         memset(use,0,sizeof(use)); //记录每个面值的硬币使用了多少。
60         for(int i=n-1;i>=0;i--)  //从大面值开始扫。
61         {    //注意这里只有一次循环,就把所有面值的使用情况扫出来了,因为如果不能用当前面值支付的话 k会变成0,并且要么会刚好支付完,要么tmp>0
62             if(p[i].second) //当前面值还有                                           不会出现浪费当前面值的情况
63             {
64                 int k=tmp/p[i].first;  //为了支付c元 当前面值最多可以用多少个,
65                 int mi=min(k,p[i].second); //取最小值
66                 tmp-=p[i].first*mi; //剩下 这么多
67                 use[i]=mi; //记录使用数量
68                 if(tmp<=0) {flag=1;break;} //tmp小于0  那么本次支付已结束
69             }
70         }
71         if(tmp>0)  //否则从小到大开始 给
72         {
73             for(int i=0;i<n;i++)
74             {
75                 if(p[i].second>use[i])  //如果没使用完
76                 {
77                     while(use[i]<p[i].second)  //一直使用,因为当前用的面值是最小的那么这样也是最优的。
78                     {
79                         tmp-=p[i].first;
80                         use[i]++;
81                         if(tmp<=0) {flag=1;break;}
82                     }
83                     if(tmp<=0&&flag==1) break; //只有  flag==1的时候 tmp<=0
84                 }
85             }
86         }
87         if(!flag) break; //已经不能完成支付
88         int mx=maxn;
89         for(int i=n-1;i>=0;i--) if(use[i]) mx=min(p[i].second/use[i],mx);  //use[i]是一次支付时当前面值使用的情况,那么p[i].second/use[i] 就表示可以支付 这么多次,并且取的是所有当中的最小值。
90         sum+=mx;
91         for(int i=n-1;i>=0;i--) if(use[i]) p[i].second-=mx*use[i]; //取用了 mx次,所以相应数量减少这么多,这样会节省很多时间,不用每次重复计算,是一次很大的优化
92     }
93     printf("%d\n",sum);
94     return 0;
95 }

 

posted @ 2015-04-14 19:36  NowAndForever  阅读(369)  评论(0编辑  收藏  举报