# HDU_5527_Too Rich

2017-10-08 08:27:16来源:cnblogs.com作者:edward108人点击

# Too Rich

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1410    Accepted Submission(s): 362

Problem DescriptionYou are a rich person, and you think your wallet is too heavy and full now. So you want to give me some money by buying a lovely pusheen sticker which costs pdollars from me. To make your wallet lighter, you decide to pay exactly p dollars by as many coins and/or banknotes as possible.

For example, if p=17 and you have two \$10 coins, four \$5 coins, and eight \$1 coins, you will pay it by two \$5 coins and seven \$1 coins. But this task is incredibly hard since you are too rich and the sticker is too expensive and pusheen is too lovely, please write a program to calculate the best solution. InputThe first line contains an integer T indicating the total number of test cases. Each test case is a line with 11 integers p,c1,c5,c10,c20,c50,c100,c200,c500,c1000,c2000, specifying the price of the pusheen sticker, and the number of coins and banknotes in each denomination. The number ci means how many coins/banknotes in denominations of i dollars in your wallet.

1≤T≤20000
0≤p≤109
0≤ci≤100000 OutputFor each test case, please output the maximum number of coins and/or banknotes he can pay for exactly p dollars in a line. If you cannot pay for exactly p dollars, please simply output '-1'. Sample Input317 8 4 2 0 0 0 0 0 0 0100 99 0 0 0 0 0 0 0 0 02015 9 8 7 6 5 4 3 2 1 0 Sample Output9-136 Source2015ACM/ICPC亚洲区长春站-重现赛（感谢东北师大）
• 首先由于p的数值范围的缘故排除用完全背包来解
• 用dfs枚举纸币张数爆搜肯定会tle
• 所以往贪心的思想考虑
• 朴素的想法是对于p从小面额的凑到大面额的纸币
• 但是这道题中纸币的面额如果以倍数为关系建图可以发现（20,50）和（200,500）会分别从10和100的节点分叉，但是其他节点均可表示比当前节点大的所有面额的纸币
• 这个特性意味着用小面额20或200有凑不出50和500以及其奇数倍数值的情况
• 反思之前提到的朴素的贪心想法
• 如果凑到最后出现凑的数额不等于p
• 则一定是最后加的当前最大面额导致的
• 补救的办法应是用之前使用的小面额纸币凑当前导致超范围的纸币的面额
• 那么如之前所述
• 10种纸币的面额不是一种很好的结构
• 不能保证小面额纸币一定可以凑成任意大面额纸币
• 就存在不可补救的情况存在
• 而且我们没法保证当前凑数的结果是正确的凑数结果
• 那有没有补救的办法呢？
• 还是有滴呀
• 只要把纸币面额更改成最优结构即可
• 就是每次使用两张50或500，就把这种面额的纸币算是取消了
• 之后就剩下8种面额的纸币，而且都满足可以凑成任意比当前面额大的纸币的条件
• 剩下的就是讨论50和500两种纸币使用张数奇偶性的问题了
• 4种情况，在贪心前加上就行
• 高中数学老师讲得好啊！“正难则反”
• 这个题就可以反向思维，只要凑出纸币剩余面额的最小张数，减一下就是结果啊！
• 如果正向考虑，应该是贪心凑数，最后对于多出来的数值用小面额先代替大面额之后打补丁
• 好吧，很麻烦
• 但是反向来做，不用代替和打补丁，就是直接用最大面值来凑，不存在打补丁的问题
` 1 #include <iostream> 2 #include <string> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #include <climits> 7 #include <cmath> 8 #include <vector> 9 #include <queue>10 #include <stack>11 #include <set>12 #include <map>13 using namespace std;14 typedef long long           LL ;15 typedef unsigned long long ULL ;16 const int    maxn = 1e5 + 10   ;17 const int    inf  = 0x3f3f3f3f ;18 const int    npos = -1         ;19 const int    mod  = 1e9 + 7    ;20 const int    mxx  = 100 + 5    ;21 const double eps  = 1e-6       ;22 const double PI   = acos(-1.0) ;23 24 int c[11]={0,1,5,10,20,50,100,200,500,1000,2000};25 int a[20], b[20], T, ans, tot;26 LL p, sum;27 int main(){28     // freopen("in.txt","r",stdin);29     // freopen("out.txt","w",stdout);30     while(~scanf("%d",&T)){31         while(T--){32             sum=0;33             tot=0;34             ans=inf;35             scanf("%lld",&p);36             for(int i=1;i<11;i++){37                 scanf("%d",&a[i]);38                 tot+=a[i];39                 sum+=a[i]*c[i];40             }41             if(sum<p){42                 printf("-1/n");43                 continue;44             }45             p=sum-p;46             memcpy(b,a,sizeof(a));47             for(int i=0;i<2;i++)48                 for(int j=0;j<2;j++)49                     if(i<=a[5] && j<=a[8]){50                         b[5]=a[5]-i;51                         b[8]=a[8]-j;52                         LL t=p-i*c[5]-j*c[8];53                         int cnt=i+j;54                         for(int k=10;k>0&&t>0;k--){55                             int del=min(b[k],(int)t/c[k]);56                             if((k==5||k==8)&&(del&1))57                                 del--;58                             t-=del*c[k];59                             cnt+=del;60                         }61                         if(t==0)62                             ans=min(ans,cnt);63                     }64             if(ans==inf)65                 ans=-1;66             else67                 ans=tot-ans;68             printf("%d/n",ans);69         }70     }71     return 0;72 }`