Allowance
Description
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. Source |
大概思路我倒是自己想出来了,就是代码我没写出来。(白说……)
最后看别人的代码,一开始没看懂后来调试了一遍懂了。那个代码写得相当巧妙!!超厉害!!
代码里面我加一些注释,最重要的还是要自己动手调试吧。调试一下思路立刻清晰了。
题意:FJ要给Bessie发零用钱,现在FJ有一些面值的硬币。他每周必须至少给Bessie发价值C的零用钱,问他最多能给发几周。
题解:先把单值大于C的零用钱直接发出去。
剩下的钱,首先从大拿到小,但是拿的钱不能多于C。等于C直接处理掉,小于C的继续下一步。
然后,再从剩下的钱里从小拿到大,这一步大于C也没关系,发出去就行。(从小开始拿,才能使超过C的部分尽量小!)
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
int n,c;
struct it
{
int money,num;
}a[25];
bool cmp(it x,it y)
{
return x.money>y.money;
}
int use[25];
int main()
{
int i;
while(~scanf("%d%d",&n,&c))
{
for(i=0;i<n;i++)
scanf("%d%d",&a[i].money,&a[i].num);
sort(a,a+n,cmp);
int sum=0;
for(i=0;i<n;i++)//把大于的直接发出去
{
if(a[i].money>=c)
{
sum+=a[i].num;
a[i].num=0;
}
}
while(1)//每次的循环,相当于凑出一个方案来。
{
int flag=0;
int temp=c;
memset(use,0,sizeof(use));//每次循环重新记录这回花了多少钱
for(i=0;i<n;i++)//因为前面把单个大于C的都取出去了,所以这一步每一个的钱肯定都不会大于C
if(a[i].num)
{
int k=temp/a[i].money;//算出在这种情况下,需要多少个a[i],记为k
int mi=min(a[i].num,k);//对比a[i]里的钱的个数和k谁小。如果k小,那么这个循环继续,继续找可以凑成的
temp-=mi*a[i].money; //如果k大的话,那么说明a[i]可以直接凑出C,这时候能跳出循环,处理这一项。
use[i]=mi;
if(temp<=0)
{
flag=1;//符合条件标记
break;
}
}
if(temp>0)//题解最后一步。
{
for(i=n-1;i>=0;i--)
if(a[i].num>use[i])//如果现有的钱大于他已经花出去的钱,那么再继续。小于等于的话,说明已经花完这部分了
{
while(use[i]<a[i].num)
{
temp-=a[i].money;//从小面额一点一点减去,能少超过C就少超过
use[i]++;
if(temp<=0)
{
flag=1;//得到符合条件,标记
break;
}
}
if(temp<=0) break;
}
}
if(!flag) break;//如果这一轮小于C(凑不够钱),直接跳出。
int mx=1000000000;
for(i=0;i<n;i++)
if(use[i])
mx=min(mx,a[i].num/use[i]);//寻找每一回里面最小的钱币数
sum+=mx;
for(i=0;i<n;i++)
if(use[i])
a[i].num-=mx*use[i];//删掉已经发出去的钱币
}
printf("%d\n",sum);
}
return 0;
}