放题:
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
拿到题就知道是贪心,但我不知道知道怎么贪心,在我看来,每种硬币的数据范围是1e6,有20种,我计算是2e7,for循环一般最多跑1e8的数据,就没敢往逐个硬币去贪心,以为是硬币种类上的贪心。但想来想去,都不对。
写了好几次,都崩了,才考虑逐个去贪心。
思路:先找到大于每周工资的硬币,这些可以先给了,因为这些硬币不用和其他硬币拼凑就比每周工资大(这一步预处理可以节省一定的时间)。然后怎么去取,我考虑是从大的和小的拼凑,但无奈太菜了,又错了几次,只好看了下题解。根据大佬的思路,我们只要从大往小取,有等于每周工资的,先给了,使之不超过每周工资,再从小到大取,若超过每周工资就可以给了。这样子就可以巧妙的完成了我原先大小拼凑的思路,我只能说学到了,自己还是太菜了,还要好好学习。
代码如下:
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define fo1(a,b) for(int a=0;a<b;++a)
#define fo2(a,b) for(int a=1;a<=b;++a)
#define lowbit(a) a&(-a)
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=2e5+5;
const int mod=1e9+7;
struct node
{
int mon,num;
bool operator < (const node b) const{
return mon<b.mon;
}
}cnt[25];
int main()
{
int n,m;
cin>>n>>m;
fo2(i,n)
scanf("%d%d",&cnt[i].mon,&cnt[i].num);
sort(cnt+1,cnt+1+n);
int cnr=n;
int sum=0,ans=0;
for(int i=n;i>=1;--i){ //预处理,超额工资就先给了
if(cnt[i].mon>=m){
ans+=cnt[i].num;
cnr--;
}
else
sum+=cnt[i].num;
}
while(sum){
int t=m;
for(int i=cnr;i>=1;--i){
if(!cnt[i].num)
continue;
if(sum>0){ //在取得最接近每周工资的所用的硬币个数
//和实际所有的硬币个数中取最小值
int te=min(t/cnt[i].mon,cnt[i].num);
t-=te*cnt[i].mon;
sum-=te;
cnt[i].num-=te;
}
if(t<=0) //满足条件跳出
break;
}
if(t>0)
for(int i=1;i<=cnr;++i){
if(cnt[i].num){ //这里的循环也可以用上面的那种方式做,
//可以节省时间
while(t>0&&cnt[i].num){
t-=cnt[i].mon;
sum--;
cnt[i].num--;
}
if(t<=0)
break;
}
}
if(t<=0)
ans++;
}
cout<<ans<<endl;
return 0;
}