As a reward for record milk production, Farmer John has decided to start paying Bessie the cow a small weekly allowance. FJ has a set of coins in N (1 <= N <= 20) different denominations, 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).Using the given set of coins, he would like to pay Bessie at least some given amount of money C (1 <= C <= 100,000,000) every week.Please help him ompute the maximum number of weeks he can pay Bessie.
Input
* Line 1: Two space-separated integers: N and C
* 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
* Line 1: A single integer that is the number of weeks Farmer John can pay Bessie at least C allowance
Sample Input
3 6 10 1 1 100 5 120
Sample Output
111
Hint
INPUT DETAILS:
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++版本一
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=30;
struct node
{
int x,y;//存储钱的大小和数量
bool operator<(const node c)const
{
return x>c.x;
}
}num[maxn];
int need[maxn],n,c,kj;
//need[]函数是是存储需要哪些钱币的组合
int main()
{
while(scanf("%d%d",&n,&c)!=EOF)
{
for(int i=0;i<n;i++)
scanf("%d%d",&num[i].x,&num[i].y);
sort(num,num+n);//排序是第一步
kj=0;
while(1)
{
int sum=c;
memset(need,0,sizeof(need));
for(int i=0;i<n;i++)//这个for循环是用于找出钱币的组合方法
{
if(sum>0)
{
int t=sum/num[i].x;
if(t==0)
continue;
t=min(t,num[i].y);
need[i]=t;
sum-=t*num[i].x;
}
}
if(sum>0)//如果在上面的组合中没有使sum变为0,则从小到大选择最小的面值组合,这样使面值超过的最小化
{
for(int i=n-1;i>=0;i--)
if(num[i].y&&num[i].x>=sum)
{
sum=0;
need[i]++;
break;
}
}
if(sum>0)//如果组合不能达到c,怎么说明搜索结束
break;
int cc=2e9;
for(int i=n-1;i>=0;i--)//找出这个组合成的面值中最小的数量,同时减去它们。
{
if(need[i])
{
cc=min(cc,num[i].y/need[i]);
}
}
kj+=cc;
for(int i=0;i<n;i++)
if(need[i])
{
num[i].y-=cc*need[i];
}
}
printf("%d\n",kj);
}
return 0;
}
C++版本二
/*大于C的coin每周一个发给Bessie就好了
小于C的:
从cent最大的开始取,凑近C但不能等于C
从cent最小的开始取,等于或大于C
*/
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX_N = 25;
int N, C, ans;
struct coin {
int cent;
int num;
} a[MAX_N];
bool cmp(coin b, coin c)
{
return b.cent > c.cent;
}
void init()
{
ans = 0;//需要吗?
for(int i=0; i<N; i++){
scanf("%d%d", &a[i].cent, &a[i].num);
}
}
void f()
{
int i, j = N-1, sum = 0;
for(i=0; i<N && a[i].cent>=C; i++){
ans += a[i].num;
a[i].num = 0;
}
out:
for(i=0; i<N; i++){
while(a[i].num>0){
//从cent最大的开始取凑近或等于C但不大于C
sum += a[i].cent;
a[i].num--;
if(sum == C){
ans++; sum = 0;
goto out;
}
else if(sum > C){
sum -= a[i].cent;
a[i].num++;
break;
}
}
}
//从cent最小的开始取直到凑够C
for(; j>=0; j--){
while(a[j].num > 0){
sum += a[j].cent;
a[j].num--;
if(sum >= C){
ans++; sum = 0;
goto out;
}
}
}
}
int main()
{
while(~scanf("%d%d", &N, &C)){
init();
sort(a, a+N, cmp);
f();
printf("%d\n", ans);//记得删\n!!!
}
return 0;
}
C++版本三
思路:第一时间肯定是想到浪费的越少越好。
1. 当币值大于C的时候,就直接用这个硬币付工资。
2. 当币值小于C的时候,就要想一种搭配方案,使得总的价值等于或者多于C但多余的钱要尽可能少,这个就比较难想到了。
而题目中有一个条件是,大的币值能被小的整除,所以若干个小的一定能凑出大的,那么可以用一个大的或若干个小的的情况下,肯定是要选用一个大的,这样之后的搭配方案才能更好的符合”超出C的钱尽可能少“这个条件。
所以, 当当币值小于C的时候,从币值最大的开始遍历,一个小于C,那两个、三个一直到n个,直到刚好等于C或者n+1个硬币大于C,如果是后者,那就选n个,在从币值更小的硬币重复上面的过程,直到刚好等于C或者全部硬币遍历完
如果最后的全部遍历完还是不能凑到刚好等于C,那么从小到大,直到找到一个加上这个硬币刚好大于C的。如果找不到,就说明剩下的全部加起来已经没有C了,结束了
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN = 25;
pair<int,int> p[MAXN];//存储硬币币值和数量
int n, c;
int main()
{
while(scanf("%d%d", &n, &c) == 2)
{
for(int i=0; i<n; i++)
scanf("%d%d", &p[i].first, &p[i].second);
sort(p, p+n);//按币值从小到大排序
int ans = 0, require, need[MAXN], rest;
while(true)
{
rest = c;
memset(need, 0, sizeof(need));
for(int i=n-1; i>=0; --i)//从大到小选择
{
if(p[i].second>0)
{
require = min(p[i].second, rest/p[i].first);//需要这个币值的硬币的数量
rest -= require*p[i].first;
need[i] = require;
}
if(rest == 0) break;//刚好凑出C
}
if(rest)
{
for(int i=0; i<n; i++)//从小到大选择
{
if(p[i].second && p[i].first>=rest)//加上这个硬币(最小)就刚好大于C
{
rest = 0;
need[i]++;
break;
}
}
}
if(rest) break;//两轮选择后仍然凑不齐C,结束
int week = 1e8;//用这个方案能发多少个星期的工资
for(int i=0; i<n; i++)
{
if(need[i]) week = min(week, p[i].second/need[i]);
}
ans+=week;
for(int i=0; i<n; i++)//把用掉的硬币减去
{
if(need[i]) p[i].second -= week*need[i];
}
}
cout << ans << endl;
}
return 0;
}