题目描述:农夫约翰要给奶牛Bessie发工资了,每周至少 C 元。约翰手头上有面值V_i的硬币B_i个,求最多能发几周?
题解:
贪心策略是使多发的面额最小(最优解)。分三个阶段:
-
首先面额不小于C的硬币属于没办法节约的类型,先统统发掉。
-
然后对硬币面额从大到小尽量凑得接近C,允许等于或不足C,但是不能超出C。
-
接着按硬币面额从小到大凑满C(凑满的意思是允许超出一个最小面值,ps此处的最小面值指的是硬币剩余量不为0的那些硬币中的最小面值),凑满之后得出了最优解,发掉,进入步骤2.
-
这样就保证了每次都是当前的最优解,这个题很好地体现了贪心法的精髓。
参考程序:
#include<cstdio>
#include<algorithm>
#include <functional>
#include <limits>
#include<string>
#include<iostream>
using namespace std;
struct Node{
int v,c;
}end;
Node coin[20];
int need[20];
bool cmp(Node a,Node b){
return (a.v>b.v);
}
int main(){
int N,C,week=0;
scanf("%d %d",&N,&C);
for (int i=0;i<N;i++)scanf("%d %d",&coin[i].v,&coin[i].c);
for (int i=0;i<N;i++)if (coin[i].v>=C)week+=coin[i].c,coin[i].c=0;
sort(coin,coin+N,cmp);
while (true){
int sum=C;
memset(need,0,sizeof(need));
for (int i = 0; i < N; ++i)
if (sum > 0 && coin[i].c > 0){
int can_use = min(coin[i].c, sum / coin[i].v);
if (can_use > 0){
sum -= can_use * coin[i].v;
need[i] = can_use;
}
}
for (int i = N - 1; i >= 0; --i)
if (sum > 0 && coin[i].c > 0){
int can_use = min(coin[i].c - need[i],(sum + coin[i].v - 1) / coin[i].v);
if (can_use > 0){
sum -= can_use * coin[i].v;
need[i] += can_use;
}
}
if(sum > 0)break;
int add_up = numeric_limits<int>::max();
for (int i = 0; i < N; ++i){
if (need[i] == 0)continue;
add_up = min(add_up, coin[i].c / need[i]);
}
week += add_up;
for (int i = 0; i < N; ++i){
if (need[i] == 0)continue;
coin[i].c -= add_up * need[i];
}
}
printf("%d",week);
return 0;
}