POJ 3040 Allowance【难,贪心】

Allowance
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 2924 Accepted: 1180

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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值