题目:POJ - 3040

题意:

农夫要给奶牛Bessie每周津贴。农夫有N种不同面额不同数量的硬币,而且相邻大小的硬币面额存在整除关系(1分、5分、10分、50分)。他每周至少要给奶牛C分钱,计算他所有的钱最多可以给奶牛多少周。

思路:

有限的钱要维持尽量多的时间,关键在于每周尽可能少的超过最低标准。因此,对所有的硬币分两类,面额大于C的硬币一天给一个。面额小于C的硬币……没有想到合适的贪心策略了。

参考了:码农场 » POJ 3040 Allowance 题解 《挑战程序设计竞赛(第2版)》

大硬币面额是小硬币面额的倍数很重要,这意味着同等数量的一堆小硬币可以被一枚大硬币代替,这样小硬币就可以剩下来以后用,可以保证最小的浪费,所以

1. 从大到小贪心,尽量多的选择大硬币,但是不要超过所需金额c

2. 从小到大贪心。

1、2都执行完之后,如果所需的金额还是大于0,就说明硬币不够再多给一周了,break。

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <algorithm>
 4 #include <math.h>
 5 
 6 using namespace std;
 7 
 8 int n;
 9 int m = 0; //需要进行贪心的硬币 
10 int c;
11 pair<int, int> coin[20];
12 
13 int solve() {
14     int res = 0;
15     sort(coin, coin + m);
16     int need = c;
17     while (1) {
18         for (int i = m - 1; i >= 0; --i) {   
19             if (need > 0 && coin[i].second > 0) {
20                 int t = min(coin[i].second, need / coin[i].first);
21                 need -= t * coin[i].first;
22                 coin[i].second -= t;  
23             }   
24         } 
25         for (int i = 0; i < m; i++) {
26             if (need > 0 && coin[i].second > 0) {
27                 int t = min(coin[i].second, (int)ceil((double)need / (double)(coin[i].first)));
28                 need -= t * coin[i].first;
29                 coin[i].second -= t;     
30             }
31             
32         }
33         if (need > 0) {
34             break;
35         }
36         res++;
37         need = c;  
38     }
39     return res;
40 }
41 
42 int main() {
43     int res = 0;
44     int v;
45     int b;
46     scanf("%d %d", &n, &c);
47     for (int i = 0; i < n; ++i) {
48         scanf("%d %d", &v, &b);
49         if (v >= c) {
50             res += b;
51         }
52         else {
53             coin[m].first = v;
54             coin[m].second = b;
55             m++;
56         }
57     }
58     res += solve();
59     
60     printf("%d\n", res); 
61     return 0;
62 }