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 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: Source |
算法分析:
题意:
农夫约翰要给奶牛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;
}