POJ 3040 Allowance

Allowance

Time Limit: 1000MS

 

Memory Limit: 65536K

Total Submissions: 5511

 

Accepted: 2168

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

USACO 2005 October Silver

算法分析:

题意:

农夫约翰要给奶牛Bessie发工资了,每周至少 m 元。约翰手头上有面值v_i的硬币c_i个,注意,每种面值的金钱都是下一种的面值的倍数。求最多能发几周?

分析:

贪心策略是使多发的面额最小(最优解)。分三个阶段:

1.    首先面额不小于m的硬币属于没办法节约的类型,先统统发掉。

2.    然后对硬币面额从大到小尽量凑得接近C,允许等于或不足C,但是不能超出C。

3.    接着按硬币面额从小到大凑满C(凑满的意思是允许超出一个最小面值,此处的最小面值指的是硬币剩余量不为0的那些硬币中的最小面值),凑满之后得出了最优解。

重复步骤2,3

这样就保证了每次都是当前的最优解,这个题很好地体现了贪心法的精髓:局部解求最优解。

代码实现:

#include<cstdio>  
#include<cstring>  
#include<cstdlib>  
#include<cctype>  
#include<cmath>  
#include<iostream>  
#include<sstream>  
#include<iterator>  
#include<algorithm>  
#include<string>  
#include<vector>  
#include<set>  
#include<map>  
#include<stack>  
#include<deque>  
#include<queue>  
#include<list>  
using namespace std;  
const double eps = 1e-8;  
typedef long long LL;  
typedef unsigned long long ULL;  
const int INF = 0x3f3f3f3f;  
const int INT_M_INF = 0x7f7f7f7f;  
const LL LL_INF = 0x3f3f3f3f3f3f3f3f;  
const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f;  
const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1};  
const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1};  
const int MOD = 1e9 + 7;  
const double pi = acos(-1.0);  
const int MAXN=5010;  
const int MAXM=100010;
struct node
{
	int c,v;
}a[25];
 bool cmp(const node &x,const node &y)
 {
 	return x.v<y.v;
 }
int main()
{
   int n,m;
   while(scanf("%d%d",&n,&m)!=EOF)
	{
	   for(int i=0;i<n;i++)
	   	scanf("%d%d",&a[i].v,&a[i].c);
	   sort(a,a+n,cmp);
	   int sum=0,minn=0,ans=0;
	   for(int i=0;i<n;i++)    //比大的全部取走 ,不用节约
	   {
	   	if(a[i].v>=m)
		 {
		 	ans+=a[i].c;
		 	a[i].c=0;
		     n--;         //大的减去
		 }
	   }
	   int use[25];     //用与记录选择取走i物品几个
	   
	  
	   while(1)
	   {
	   	memset(use,0,sizeof(use));     //注意use清0
	   	 sum=m;
	   	 for(int i=n-1;i>=0;i--)    //从大到小筛选
		 {
		 	if(sum>0&&a[i].c>0)
			{
			  int cnt=min(a[i].c,sum/a[i].v); //凑够sum,但不能超过
			                                   //考虑a[i].v是否够不够
			  if(cnt>0)
			  {
			  	sum-=a[i].v*cnt;
			  	use[i]=cnt;
			  }	
			}
		 }
	
		 for(int i=0;i<n;i++)
		 {
		 	if(sum>0&&a[i].c>0)
			{
				int cnt=min(a[i].c-use[i],(sum+a[i].v-1)/a[i].v);//凑够sum,只能超过当前货币的最小面值 ,最小面币值为1(对于全部),这样起到了保证最多超过一个最小面逼值 ,很秒的
				if(cnt>0)
			  {
			  	sum-=a[i].v*cnt;
			  	use[i]+=cnt;
			  }	
			}
		 }
		 if(sum>0) break;
		 
		 int add=INF;    //记录有几个这样的情况,都取走即可
		 for(int i=0;i<n;i++)
		 {
		 	if(use[i]!=0)
			{
			add=min(add,a[i].c/use[i]);  //很妙,很妙,add保证了全部物品的剩余量均够,一个不够取 最小的
			}
		 }
		 ans+=add;
		 for(int i=0;i<n;i++)
		 {
		 	if(use[i]!=0)
			{
			 a[i].c-=add*use[i];
			}
		 }
	   }
	   cout<<ans<<endl;

	}
   return 0;
}

猜你喜欢

转载自blog.csdn.net/sdz20172133/article/details/81272734