POJ 3040 Allowance(贪心,诠释思想的好题)

Allowance

Time Limit: 1000MS


Memory Limit: 65536K

Total Submissions: 2300


Accepted: 945

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.

题意:约翰要给他的牛贝西发工资,每天不得低于C元,约翰有n种面值的钱币,第i种的面值为v_i,数量有b_i。问这些钱最多给贝西发多少天的工资。注意,每种面值的金钱都是下一种的面值的倍数。

题解:好题,深入了解贪心思想。POJ真不错啊,刷个贪心都把我搞得不要不要的(;′⌒`)。这一题分三步解决:

     1. 按照面值从大到小取,面值大于等于C的,直接取光。

     2. 再按面值从大到小取,凑近C,可以小于等于C,但不能大于C。
    
     3.最后从小到大取,凑满C,这里的凑满可以等于大于C。然后将上述2,3步取到的面值全部取走,再转入步骤2,这样每次找到的取法就是当前最优取法,直到所剩下的金币总价值不够C结束。

很好的题目,很能表现贪心思想:从局部的最优解找出总体最优解。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f 
using namespace std;
int use[30];//记录当前取法的第i种面值取的个数 
struct node
{
	int v,b;
}a[25];

int cmp(node a,node b)
{
	return a.v<b.v;
} 

int main()
{
	int n,c,i,cnt,ans,k,m;
	while(scanf("%d%d",&n,&c)!=EOF)
	{
		for(i=0;i<n;++i)
			scanf("%d%d",&a[i].v,&a[i].b);
		sort(a,a+n,cmp);
		ans=0;
		for(i=n-1;i>=0;i--)//第一步,满足大于C的面值全部取走 
		{
			if(a[i].v>=c)
			{
				ans+=a[i].b;
				a[i].b=0;
			}
		}
		while(1)//每次循环都在找一次当前最优取法,直到剩下的总金额小于C元 
		{
			int sign=0;
			cnt=c;
			memset(use,0,sizeof(use));
			for(i=n-1;i>=0;--i)//第二步,从大到小取,不能超过C的值 
			{
				if(a[i].b)
				{
					k=cnt/a[i].v;
					m=min(k,a[i].b);
					cnt-=m*a[i].v;
					use[i]=m;
					if(cnt==0)
					{
						sign=1;
						break;
					}
				}
			}
			if(cnt>0)
			{
				for(i=0;i<n;++i)//第三步,从小到大取,凑满C 
				{
					if(a[i].b>use[i])
					{
						while(use[i]<a[i].b)
						{
							cnt-=a[i].v;
							use[i]++;
							if(cnt<=0)
							{
								sign=1;
								break;
							}
						}
					}
					if(sign)
						break;
				}
			}
			if(!sign)
				break;
			m=INF;
			for(i=0;i<n;++i)
			{
				if(use[i])//找到当前取法的能取的总次数 
					m=min(m,a[i].b/use[i]);
			}
			ans+=m;
			for(i=0;i<n;++i)
			{
				if(use[i])
					a[i].b-=m*use[i];
			} 
		}
		printf("%d\n",ans);
	}
	return 0;
}






  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值