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 }