poj3040 Allowance 题解报告
【题目大意】
有$n$种面值的硬币,第$i$种有$b_i$个,大的硬币是小的硬币的倍数,给定一个金额$k$,求最多可以组成多少份金额不小于$k$的组合。
【思路分析】
首先去掉能自成一组的硬币,然后考虑几种硬币凑成一组,贪心策略就是优先考虑面值大的,细节见代码。
【代码实现】
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define g() getchar() 7 #define rg register 8 #define go(i,a,b) for(rg int i=a;i<=b;i++) 9 #define back(i,a,b) for(rg int i=a;i>=b;i--) 10 #define db double 11 #define ll long long 12 #define il inline 13 #define pf printf 14 #define mem(a,b) memset(a,b,sizeof(a)) 15 using namespace std; 16 ll fr(){ 17 ll w=0,q=1; 18 char ch=g(); 19 while(ch<'0'||ch>'9'){ 20 if(ch=='-') q=-1; 21 ch=g(); 22 } 23 while(ch>='0'&&ch<='9') w=(w<<1)+(w<<3)+ch-'0',ch=g(); 24 return w*q; 25 } 26 const int N=22; 27 ll n,c,ans,use[N]; 28 struct money{ 29 ll v,b; 30 }coin[N]; 31 bool tag; 32 bool cmp(money A,money B){ 33 return A.v<B.v; 34 } 35 int main(){ 36 //freopen("","r",stdin); 37 //freopen("","w",stdout); 38 n=fr();c=fr(); 39 go(i,1,n) coin[i].v=fr(),coin[i].b=fr(); 40 sort(coin+1,coin+1+n,cmp); 41 back(i,n,1){//先把可以单独一组的去掉 42 if(coin[i].v<c) break; 43 ans+=coin[i].b; 44 coin[i].b=0; 45 } 46 while(1){ 47 mem(use,0); 48 tag=0; 49 rg ll t=c,m;//t记录当前组成一组还需要的钱数 50 back(i,n,1){ 51 if(coin[i].b){ 52 rg ll num=t/coin[i].v; 53 m=min(num,coin[i].b); 54 t-=m*coin[i].v; 55 use[i]=m;//记录使用了多少个此面值的硬币 56 if(t==0) {tag=1;break;}//如果组成一组了就退出 57 } 58 } 59 if(t>0){//无法凑整的情况 60 go(i,1,n){ 61 if(coin[i].b>use[i]){//用有剩余的硬币来补 62 while(use[i]<coin[i].b){ 63 if(t<0) {tag=1;break;} 64 } 65 } 66 if(tag) break; 67 } 68 } 69 if(!tag) break;//无法凑成一组的情况 70 rg ll as=1e9+7; 71 go(i,1,n) if(use[i]) as=min(as,coin[i].b/use[i]); 72 //考虑到有几种硬币凑成一组的情况所以要取最小值 73 ans+=as; 74 go(i,1,n) coin[i].b-=use[i]*as; 75 } 76 pf("%lld\n",ans); 77 return 0; 78 }