zoukankan      html  css  js  c++  java
  • 【HDOJ5527】Too Rich(贪心,构造)

    题意:给定10种面额的货币和它们的数量上限,问构造出恰好总额为P的最小张数,无解输出-1

    T=2e4,p<=1e9,c[i]<=1e5

    思路:From https://blog.csdn.net/snowy_smile/article/details/49592521

    如果采用最傻瓜式的贪心,我们一定是使得每枚货币的面值尽可能低。
    即,如果有面值更低的硬币,我们优先使用它;它用完之后,才考虑面值更大的硬币。
    这种做法显然有问题。因为它不能保证我们恰好凑得硬币的总价值为m。
    1,可能不够——直接-1
    2,可能会溢出,这个要怎么办呢?
    我们发现,我们可以算得溢出的硬币面值more。
    如果我们能求出,之前选取硬币中,凑成面值总额为more的最少硬币个数,这样减一下即可得到答案?
    但是这里就不能再继续傻瓜下去啦,我们可能会面临一个问题——
    尽管我们之前的做法,留给了我们回退的余地,但是其依然有可能出现无法回退,错误判定为无解的状况。
    比如:我们有50元硬币*1,20元硬币*3,我们想要凑得面额为50的硬币。
    这种做法会先算入20元硬币*3,然后不论怎么回退都没有办法达成目的。
    为什么会出现这种情况呢?
    我们发现,这是因为,对于给定的所有面值,
    每个面值都是比它大的所有面值的约数,除了(20<->50),(200<->500)这两个关系。
    如果不存在这两个特殊关系,那么我们可以就采取一开始的贪心原则。
    因为小的面值是比它大的所有面值的因子,所以大硬币所能拼凑的面额它一定能拼成。
    也就是说,它不仅在数量上保证了更多,也在拼凑额度的功能上更优。
    所以这时可以贪心:永远都拿面额更小的,直到面额>=m。
    这里还是会出现面额可能超出的情况,但是首先超出的是大面额。而我们所有的小面额都取了。
    而这时,我们操作的灵活性会是最大的,接下来的调整一定可以完成。
    如果采取这个贪心做下去,那就只需要解决这两个特殊关系啦。
    如何解决呢?
    我们发现问题就是:
    50可能取奇数次,这是20所凑不出的。
    500可能取奇数次,这是200所凑不出的。
    于是我们枚举以下四种情况
    (50和500都是偶数个,不先取50和500)
    (50为奇数个,500为偶数个,即我们先取一个50)
    (50为偶数个,500为奇数个,即我们先取一个500)
    (50为奇数个,500为奇数个,即我们先取一个50和一个500)
    之后对于50和500都成对地取。
    每次取50或500的时候就取偶数个。然而消除的时候也消除偶数个。
    这样的枚举,就消除了这个两个特殊关系的影响啦。
    为什么呢,我们可以分析下:
    先以50为例,我们先枚举最后取50的奇偶性,再贪心从小到大取数,然后溢出了,开始考虑移除(移除最少的个数)——
    不论最后取的50是奇数个还是偶数个,对于每个成对的50(即100)而言,
    结合之间的"约数结论",只要我们剩余的数的总和大于等于100,那么它们是一定能够拼凑出100的。
    于是我们有:这里移除2个50肯定是移除最少的个数,是最优的。
    同理,对于500而言,先决定它最后的奇偶性。
    然后对于每个成对的500,之前的所有数都是1000的因子。如果数值之和达到1000,便一定能凑得1000。
    于是只要保证"之下的所有数的数值之和不减小到不够",这里就可以直接移除这个1000,肯定更优。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstdlib>
      4 #include<cstring>
      5 #include<ctime>
      6 #include<cmath>
      7 #include<algorithm>
      8 #include<iomanip>
      9 #include<vector>
     10 #include<map>
     11 #include<set>
     12 #include<bitset>
     13 #include<queue>
     14 #include<stack>
     15 using namespace std;
     16 typedef long long ll;
     17 typedef unsigned int uint;
     18 typedef unsigned long long ull;
     19 typedef pair<int,int> PII;
     20 typedef vector<int> VI;
     21 #define fi first
     22 #define se second
     23 #define MP make_pair
     24 #define N   20
     25 #define M   110000
     26 #define eps 1e-8
     27 #define pi  acos(-1)
     28 #define oo  1000000000
     29 #define MOD 10007
     30 
     31 const ll c[11]={0,1,5,10,20,50,100,200,500,1000,2000};
     32 
     33 int a[N],b[N],d[N],n,ans;
     34 
     35 ll min(ll x,ll y)
     36 {
     37     if(x<y) return x;
     38     return y;
     39 }
     40 
     41 int isok(ll s,int p)
     42 {
     43     int num=0;
     44     for(int i=p;i>=1;i--)
     45     {
     46         if(d[i]==-1)
     47         {
     48             int t=min(b[i],s/c[i]);
     49             num+=t;
     50             s-=t*c[i];
     51         }
     52          else
     53          {
     54              int t=min(b[i],s/c[i]);
     55              if(t&1) t--;
     56              num+=t;
     57              s-=t*c[i];
     58          }
     59     }
     60     if(s==0) return num;
     61     return -1;
     62 }
     63     
     64 void solve()
     65 {
     66     if(d[5]>a[5]||d[8]>a[8]) return;
     67     memset(b,0,sizeof(b));
     68     int s1=d[5]+d[8];
     69     ll s2=d[5]*50+d[8]*500;
     70     for(int i=1;i<=10;i++)
     71     {
     72         if(d[i]==-1)
     73         {
     74             s1+=a[i];
     75             b[i]=a[i];
     76             s2+=c[i]*a[i];
     77         }
     78          else
     79          {
     80              b[i]=a[i]-d[i];
     81              if(b[i]&1) b[i]--;
     82              s1+=b[i];
     83              s2+=c[i]*b[i];
     84          }
     85         if(s2>=n)
     86         {
     87             int t=isok(s2-n,i);
     88             if(t!=-1)
     89             {
     90                 s1-=t;
     91                 ans=max(ans,s1);
     92             }
     93             return;
     94         }
     95     }
     96 }
     97                        
     98             
     99 int main()
    100 {
    101     //freopen("hdoj5527.in","r",stdin);
    102     //freopen("hdoj5527.out","w",stdout); 
    103     int cas;
    104     scanf("%d",&cas);
    105     for(int v=1;v<=cas;v++)
    106     {
    107         scanf("%d",&n);
    108         for(int i=1;i<=10;i++) scanf("%d",&a[i]);
    109         memset(d,-1,sizeof(d));
    110         ans=-1;
    111         d[5]=0; d[8]=0; solve();
    112         d[5]=0; d[8]=1; solve();
    113         d[5]=1; d[8]=0; solve();
    114         d[5]=1; d[8]=1; solve();
    115         printf("%d
    ",ans); 
    116     }
    117     return 0;
    118 }
    119     
    120     
  • 相关阅读:
    python_day06(ip代理池)
    二叉树的层次遍历之队列的使用
    推荐系统实战笔记 1.1什么是推荐系统
    牛顿法求平方根可拓展
    java LinkedHashMap实现LRUCache缓存
    交换两个变量常规四种做法
    交换两个变量之移位交换法
    推荐系统实战笔记01--前言
    Ubuntu 14.04更新为国内阿里源解决apt-get install无法执行的问题
    求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
  • 原文地址:https://www.cnblogs.com/myx12345/p/9880506.html
Copyright © 2011-2022 走看看