贪心,从大到小排序,只要不超额就能放多少就放多少,最后再从小的开始找一个放进去能超额的。
正确性证明,因为大的是小的倍数,所以大的放进去不超额一定要放进去,因为小的不管怎么取,再超过c之前一定会凑成这个大的面额,那么用大的代替一定更优。
第一步做完之后,那么现在一定要再放进去一个硬币,那么选择最小的并且能大于c的也一定是最优的。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=22; int need[maxn]; struct C { int v,b; bool operator <(const C & xx) const { return v>xx.v; } }coin[maxn]; int main() { int n,c; while(scanf("%d %d",&n,&c)!=EOF) { for(int i=1;i<=n;i++) scanf("%d %d",&coin[i].v,&coin[i].b); sort(coin+1,coin+1+n); int ans=0; while(1) { memset(need,0,sizeof(need)); int sum=c; for(int i=1;i<=n;i++) { int tmp=sum/coin[i].v; tmp=min(coin[i].b,tmp); need[i]=tmp; sum-=tmp*coin[i].v; } if(sum>0) for(int i=n;i>=1;i--) if(coin[i].b&&coin[i].v>=sum) { need[i]++; sum=0; break; } if(sum>0) break; int minm=2e9; for(int i=1;i<=n;i++) if(need[i]) minm=min(coin[i].b/need[i],minm); ans+=minm; for(int i=1;i<=n;i++) if(need[i]) coin[i].b-=need[i]*minm; } cout<<ans<<endl; } return 0; }