挑战程序设计竞赛2.2习题:Allowance POJ - 3040
Posted jacobfun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了挑战程序设计竞赛2.2习题:Allowance POJ - 3040相关的知识,希望对你有一定的参考价值。
Allowance
Input
* Lines 2..N+1: Each line corresponds to a denomination of coin and contains two integers: the value V (1 <= V <= 100,000,000) of the denomination, and the number of coins B (1 <= B <= 1,000,000) of this denomation in Farmer John‘s possession.
Output
Sample Input
3 6 10 1 1 100 5 120
Sample Output
111
Hint
FJ would like to pay Bessie 6 cents per week. He has 100 1-cent coins,120 5-cent coins, and 1 10-cent coin.
OUTPUT DETAILS:
FJ can overpay Bessie with the one 10-cent coin for 1 week, then pay Bessie two 5-cent coins for 10 weeks and then pay Bessie one 1-cent coin and one 5-cent coin for 100 weeks.
但是,大部分情况可能没有办法被整数倍的最小面额整除,那么必定会产生剩余,其实也是一样的,先取使得小于等于C,由于此类情况必定无法满足恰好(因为不能整除)所以肯定要选一张最小且能使得值大于C的面额,取完的结束条件同上。(见下图)
为什么要把它分成无数个最小非一的面额格子呢?我们可以假设每次取一张纸币时就是取了一份格子数,如果能恰好取到C,则从大到小从小到大均可以做到,但是从小到大的话后面每次取到的格子份数大,可能会出现浪费,所以从大到小取不会浪费,参考白书贪心例题硬币问题。
如果最小面额是一,那么在找到第一次可行的方法时,如果不用一无法达到C的话,此时可以用面额为一的来补。如果不够也能够使得最后找到的结果大于C的最小,所以如果存在面额为一,我们也可以假定面额最小是第一个比一大的,然后从小到大补齐的时候用一补齐缺口,如果补不齐也可以再寻找下一个比一大的使得值大于C来满足条件。
总结:先把面额排序,面额大于C的每次用一张能用完,小于C的从大到小遍历寻找解决方案,遍历时保证每种面额取值最大且均使得加入后值小于等于C,此时若小于C则不可能刚好到达C,从小到大选取一张最小且满足条件的面额,由此得到方案,计算这样能操作多少次,然后再重复本方案直到所有面额用完也无法大于等于C结束。
AC代码:
#include <stdio.h> #include <algorithm> #include <string.h> using namespace std; const int INF = 0x3fffffff; struct Node{ int v; int p; friend bool operator <(Node x, Node y) { return x.v < y.v; } }money[25]; int used[25];//本次方法每种钱使用的张数 int ans; int start = -1;//如果全大于c则不需要管小于c的,因为没有 int main(void) { int n,c; scanf("%d%d", &n, &c); for(int i = 0; i < n; i++) { scanf("%d %d", &money[i].v, &money[i].p); } sort(money, money + n); for(int i = n - 1; i >= 0; i--) { if(money[i].v >= c) { ans += money[i].p; money[i].p = 0; } else { start = i;//面额小于C的纸币的最大序号,在start之后就都是比C大的纸币 break; } } while(1) { memset(used, 0, sizeof(used));//每种方法找之前每张钱币都没用过 int goal = c;//要找到值至少为C for(int i = start; i >= 0; i--) { if(money[i].p == 0) continue; int t = goal / money[i].v;//相当于每次该份的最大取走份数且能使得剩余的goal >= 0 used[i] = min(money[i].p, t); goal -= used[i] * money[i].v; if(goal == 0)//能刚好取完,就完成了一种方法 break; } if(goal > 0)//没办法使用现有条件刚刚好达到C { for(int i = 0; i <= start; i++)//把从小到大全加上直到刚好大于C为止 { if(money[i].p > used[i]) { int temp = min(money[i].p - used[i], (goal + money[i].v - 1) / money[i].v); // (goal + money[i].v - 1) / money[i].v就是假设第i种数量无限,要取几张能使得值恰好大于C goal -= money[i].v * temp; used[i] += temp; } } } if(goal > 0)//所有钱币都用完了还是没办法大于等于C break; int maxcopy = INF;//判断这样的操作能搞几次 for(int i = 0; i <= start ; i++) if(used[i]) maxcopy = min(maxcopy, money[i].p / used[i]); for(int i = 0; i <= start ; i++) money[i].p -= maxcopy * used[i]; ans += maxcopy; } printf("%d ",ans); return 0; }
以上是关于挑战程序设计竞赛2.2习题:Allowance POJ - 3040的主要内容,如果未能解决你的问题,请参考以下文章
挑战程序设计竞赛3.1习题:Moo University - Financial Aid POJ - 2010
挑战程序设计竞赛2.3习题:Cheapest Palindrome POJ - 3280
挑战程序设计竞赛2.3习题:Cow Exhibition POJ - 2184
挑战程序设计竞赛3.2习题:Bound Found POJ - 2566