POJ 3040 Allowance
2015-08-18 18:50
323 查看
POJ 3040 Allowance //引用码农场
农夫约翰要给奶牛Bessie发工资了(你们结婚吧,生个牛头人( ̄_ ̄|||) ),每周至少 C 元。约翰手头上有面值V_i的硬币B_i个,这些硬币的最小公约数为硬币的最小面值。求最多能发几周?
贪心策略是使多发的面额最小(最优解)。分三个阶段:
首先面额不小于C的硬币属于没办法节约的类型,先统统发掉。
然后对硬币面额从大到小尽量凑得接近C,允许等于或不足C,但是不能超出C。
接着按硬币面额从小到大凑满C(凑满的意思是允许超出一个最小面值,ps此处的最小面值指的是硬币剩余量不为0的那些硬币中的最小面值),凑满之后得出了最优解,发掉,进入步骤2.
这样就保证了每次都是当前的最优解,这个题很好地体现了贪心法的精髓。
不过话说回来,约翰你那么喜欢奶牛,你们干脆结婚算了吧
……
农夫约翰要给奶牛Bessie发工资了(你们结婚吧,生个牛头人( ̄_ ̄|||) ),每周至少 C 元。约翰手头上有面值V_i的硬币B_i个,这些硬币的最小公约数为硬币的最小面值。求最多能发几周?
贪心策略是使多发的面额最小(最优解)。分三个阶段:
首先面额不小于C的硬币属于没办法节约的类型,先统统发掉。
然后对硬币面额从大到小尽量凑得接近C,允许等于或不足C,但是不能超出C。
接着按硬币面额从小到大凑满C(凑满的意思是允许超出一个最小面值,ps此处的最小面值指的是硬币剩余量不为0的那些硬币中的最小面值),凑满之后得出了最优解,发掉,进入步骤2.
这样就保证了每次都是当前的最优解,这个题很好地体现了贪心法的精髓。
不过话说回来,约翰你那么喜欢奶牛,你们干脆结婚算了吧
……
#include <iostream> #include <functional> #include <algorithm> #include <limits> using namespace std; typedef pair<int, int> Coin; // 硬币 面值和数量 Coin coin[20]; int need[20]; int main() { int N, C; cin >> N >> C; for (int i = 0; i < N; ++i) { cin >> coin[i].first >> coin[i].second; } int week = 0; // 面额不小于C的一定可以支付一周 for (int i = 0; i < N; ++i) { if (coin[i].first >= C) { week += coin[i].second; coin[i].second = 0; } } //用greater实现从大到小的排序 sort(coin, coin + N, greater<Coin>()); while(true) { int sum = C; // 等待凑足的sum //设置好硬币的消耗量来更新每次硬币的用量 memset(need, 0, sizeof(need)); // 从大到小 for (int i = 0; i < N; ++i) { if (sum > 0 && coin[i].second > 0) { int can_use = min(coin[i].second, sum / coin[i].first); //因为后面更新后会出现硬币不足的情况 if (can_use > 0) { sum -= can_use * coin[i].first; need[i] = can_use; } } } // 从小到大,只需要颠倒一下循环变量的初始值就轻松解决了 for (int i = N - 1; i >= 0; --i) { if (sum > 0 && coin[i].second > 0) { // 上个loop用掉了一些,因为可能判断的是相同的硬币 // 允许多出不超过一个面值的金额(好好学习一下这个表达式) int can_use = min(coin[i].second - need[i], (sum + coin[i].first - 1) / coin[i].first); if (can_use > 0) { sum -= can_use * coin[i].first; need[i] += can_use; } } } //凑不出来钱了 if(sum > 0) { break; } int add_up = numeric_limits<int>::max(); // 凑起来的week数 // add_up多少个最优的week 受限于 每种面值能满足最优解下的需求个数多少次 for (int i = 0; i < N; ++i) { if (need[i] == 0) { continue; } //相似短板效应,这种模式能持续的时间 add_up = min(add_up, coin[i].second / need[i]); } week += add_up; // 最优解生效,更新剩余硬币数量 for (int i = 0; i < N; ++i) { if (need[i] == 0) { continue; } coin[i].second -= add_up * need[i]; } } cout << week << endl; return 0; }
相关文章推荐
- POJ 3125 Printer Queue 数据结构 队列
- android中volley框架实现图片加载
- matlab初试牛刀__小练习
- Android Studio常用快捷键(个人亲测,常用!)
- (1.2.5.4.1)二叉排序树的相关算法
- LINUX下安装TFTP
- 前端获取url参数
- hdu3468 Treasure Hunting 二分匹配
- Enigma Sim-英格玛密码机模拟器
- 对于集合的选择
- ACdream 1125(ACfun-字典序)
- vim cheat sheet
- Prime Ring Problem(dfs水)
- ListView和GridView的item自动改变边距!
- HDU 1160 FatMouse's Speed
- HZNUOJ 1804 邮局选址问题
- 2005-2006 ACM-ICPC East Central North America Regional Contest (ECNA 2005) F.Square Count
- 实现最大堆
- XCode Hugging Priority- Compress Resistance Priority
- 1.3.7 Wooden Sticks