题意:作为创纪录的牛奶生产的奖励,农场主约翰决定开始给Bessie奶牛一个小的每周津贴。FJ有一套硬币N种(1≤N≤20)不同的面额,每枚硬币是所有比他小的硬币面值的倍数,例如1美分硬币、5美分硬币、10美分硬币和50美分硬币。使用这些硬币,FJ每周至少给Bessie C(1 <= C <=100000000)美分。请你计算他最多能给Bessie几周。
题解:
①因为数据量较大,所以不能采用单组模拟的方式,不然必会tle,因此设立了一个used[i]数组来存储一种贪心情况,各种硬币所需要的个数,以便将这种情况的总数一次性加在总周数上。
②贪心策略采用在满足先用大额面值,再用小额面值的前提下,先尽可能接近而不超过规定的津贴值;然后用从小额面值到大额面值,超过但尽可能少超过规定的津贴值进行贪心。
注意:
①面值大于津贴的情况,直接在输入处预处理即可。
②在考虑used数组时,不要大于本身的数量
#include<stdio.h> #include<algorithm> #include<iostream> using namespace std; typedef long long ll; const ll inf = 0x3f3f3f3f3f3f3f3f; struct coin { ll value; ll num; }p[30]; bool cmp(coin a, coin b) { return a.value > b.value; } int main(void) { ios::sync_with_stdio(false); int N, C;cin >> N >> C; ll v, n, tot = 1; ll week = 0; for (ll i = 1; i <= N; i++) { cin >> v >> n; if (v >= C) week += n; else { p[tot].value = v; p[tot].num = n; tot++; } } sort(p + 1, p + 1 + tot, cmp); ll used[30];//存储一次贪心后的结果 while (1) { memset(used, 0, sizeof(used)); ll tmp = C; ll sum = 0; for (ll i = 1; i <= tot; i++)//贪心策略1,面额从大到小,尽可能接近C,但不超过 { if (p[i].num == 0)continue; sum += p[i].num * p[i].value; used[i] = min(tmp / p[i].value, p[i].num); tmp -= used[i] * p[i].value; } if (sum < C)break; if (tmp != 0)//贪心策略2,面额从小到大,尽可能少超过C { for (ll i = tot; i >= 1; i--) { if (p[i].num == 0)continue; if (used[i] == p[i].num)continue; while (used[i] < p[i].num) { used[i]++; tmp -= p[i].value; if (tmp <= 0) break; } if (tmp <= 0) break; } } ll Min_num = inf;//Min_num表示能执行的这一组used的总数 for (ll i = 1; i <= tot; i++) { if (used[i] == 0)continue; Min_num = min(Min_num, p[i].num/used[i]); } week += Min_num; for (int i = 1; i <= tot; i++) p[i].num -= Min_num * used[i]; } cout << week << endl; return 0; }