poj 3040 Allowance (贪心
作为创纪录的牛奶生产的奖励,农场主约翰决定开始给Bessie奶牛一个小的每周津贴。FJ有一套硬币N种(1≤N≤20)不同的面额,每枚硬币是所有比他小的硬币面值的倍数,例如1美分硬币、5美分硬币、10美分硬币和50美分硬币。使用这些硬币,FJ每周至少给Bessie C(1 <= C <=100000000)美分。请你计算他最多能给Bessie几周 Input * 第一行N 、 C * 第 2..N+1行: 硬币价值 V (1 <= V <= 100,000,000) 和数量 B (1 <= B <= 1,000,000) Output * 使用这些硬币,每周至少给C美分的前提下的最多周数 Sample Input 3 6 10 1 1 100 5 120 Sample Output 111
此题贪心情况有三个:
1. 对于硬币面值大于c的,贪心情况是不搭配别的一次只发一个损失最小,先把这些发完
2. 对于可以凑成c的硬币搭配,参与组合的硬币面值越大,所用的硬币越少,剩下的硬币搭配的可能性更多,先把这些发完
3. 对于1.2情况处理后,用处理2的思路找出最接近c的硬币搭配,再从小到大找最后一枚硬币(已用2的思路贪心处理过了,结果已尽可能接近c而不到c,最后一枚参与的硬币越小,损失约越小)
注意2.3贪心情况的实现,凑c的循环可共同利用
#include<cstdio> #include<algorithm> using namespace std; const int inf=0x3f3f3f3f; struct node{ int x,n; int m,mn; bool operator < (const node &rsh) const{ return x<rsh.x; } }a[30]; int main(){ int n,c,ans=0; scanf("%d%d",&n,&c); for(int i=0 ; i<n ; i++)scanf("%d%d",&a[i].x,&a[i].n),a[i].m=0; sort(a,a+n); for(int i=n-1 ; i>=0 ; i--){ //大于c的硬币 if(a[i].x>=c&&a[i].n>0) ans+=a[i].n,a[i].n=0; } int flag=1; while(flag){ int mm=c,mn=inf; for(int i=n-1 ; i>=0 ; i--){ //从大到小尽力填充 a[i].m=0; if(a[i].n){ a[i].m=min(a[i].n,mm/a[i].x); mm-=a[i].m*a[i].x; } if(mm==0)break; } if(mm>0){ //填充不了正好c,但此时就算是最小的硬币也尽力填充了,决定是否大于c的只是最后的 一 枚硬币 for(int i=0 ; i<n ; i++){ //用面值最小的硬笔开始填充,损失最小的贪心选择 if(a[i].x>mm&&(a[i].n-a[i].m)){ a[i].m++; mm-=a[i].x; break; } } } if(mm>0)break; for(int i=0 ; i<n ; i++)if(a[i].m)mn=min(mn,a[i].n/a[i].m); ans+=mn; for(int i=0 ; i<n ; i++)if(a[i].m)a[i].n-=mn*a[i].m; } //for(int i=0 ; i<n ; i++)printf("%d %d\n",a[i].x,a[i].n); printf("%d",ans); return 0; }