(一般)POJ-3040 贪心,硬币问题

来源:互联网 发布:美工多少钱一个月 编辑:程序博客网 时间:2024/04/29 12:01

题目大意:夫约翰要给奶牛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 secondusing 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 };        //记录每次你用了多少种coinwhile (1){int sum = c;memset(need, 0, sizeof need);for (int j = i; j < n; ++j)         //先从大到小取,但不要超过cif (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;}


0 0
原创粉丝点击