标签:
题目大意:夫约翰要给奶牛Bessie发工资了(你们结婚吧,生个牛头人( ̄_ ̄|||) ),每周至少 C 元。约翰手头上有面值V_i的硬币B_i个,这些硬币的最小公约数为硬币的最小面值。求最多能发几周?
题目链接:点击打开链接
分析:此贪心解释如下:
1:大于c的就直接取;
2:如果小于就从大到小拿钱,能拿多少拿多少,但不能超过c;
3:如果2拿的钱小于c,就从小到大拿钱,能拿多少拿多少,但要求是超过c的时候是最小的,也就是说,这些钱的总和是大于c的最小数;
4:将此类取钱方式求出次数,加到count中,然后返回第二步。
注意到可以这样采取贪心的前提:where each denomination of coin evenly divides the next-larger denomination (e.g., 1 cent coins, 5 cent coins, 10 cent coins, and 50 cent coins),意思是将硬币按价值从小到大排序后,满足第i+1个为第i个的倍数。具体为什么我也搞不太清,只能猜到涉及到了数论方面的知识,记住吧。。
附上代码:
#include <cstdio> #include <cstring> #include <algorithm> #define v first #define b second using namespace std; typedef pair<int, int> node; bool cmp(node num1, node num2){ return num1.v > num2.v; } int main() { int n, c; node val[30]; scanf("%d%d", &n, &c); for (int i = 0; i < n; ++i) scanf("%d%d", &val[i].v, &val[i].b); sort(val, val + n, cmp); //从大到小排序 int i = 0; int count = 0; for (; i < n; ++i) //完全大于c的就直接用 if (val[i].v >= c) { count += val[i].b; val[i].b = 0; } else break; int need[30] = { 0 }; //记录每次你用了多少种coin while (1) { int sum = c; memset(need, 0, sizeof need); for (int j = i; j < n; ++j) //先从大到小取,但不要超过c if (val[j].b && sum > 0) { need[j] += min(val[j].b, sum / val[j].v); //取最小的个数,因为不超过,所以向下进位 sum -= need[j] * val[j].v; //减去已用了的 } if (sum > 0) for (int j = n - 1; j >= i; --j) //如果上面那次取法不够,就从小往大找补替,这样保证是最小损失 if (val[j].b && sum > 0) { int mi; mi = min(val[j].b - need[j], (sum + val[j].v - 1) / val[j].v); //取最少的,因为要超过或达到,所以向上进位 if (mi > 0) //这儿有可能是负数 { sum -= mi * val[j].v; need[j] += mi; } } if (sum > 0) break; //如果已经不够了,退出循环 int x = 1e9 + 1; for (int j = i; j < n; ++j) //算是一种剪枝,将重复情况一次做完 if (need[j]) x = min(val[j].b / need[j], x); //取最小的每次需要的种类的次数 count += x; for (int j = i; j < n; ++j) if (need[j]) val[j].b -= x * need[j]; //将用过的删除 } printf("%d\n", count); return 0; }
标签:
原文地址:http://blog.csdn.net/ac_hell/article/details/51344764