• [bzoj3233] [Ahoi2013]找硬币


      一开始没什么思路...后来想到确定最大硬币面值就知道其他面值能取多少了。。而且结果是可以由较小的面值转移过来的。

      f[i]表示最大面值为i时的最小硬币数。a[i]表示第i个物品的价钱。

      f[i]=min{   f[ i / j ]- sum{  a[ k ] / i * ( j-1 )  }   },(j是i的因数)

        对于物品k,我们能用a[k]/i 枚面值为i的硬币。显然肯定都用上啦。

        而且现在我们用面值i 的硬币拼出来的价钱,本来肯定都是用面值为( i / j )的硬币拼的。(因为j是i的因数,所以能用( i / j )的拼;又因为( i / j )是之前的最大面额,所以肯定会用)

        所以我们用a[k]/i枚面值为i 的硬币能够减少a[k]/i*(j-1)枚面值为( i / j )的硬币的使用。(感谢JSZX11556老司机指正。。已修改)

      然后又因为显然,在符合条件的情况下面值种类越多越好(至少不会更差)。。所以我们使j为质数就好了。(若j是合数的话,我们可以先增加其他面额,然后从另一种面值转移到i,所以j不取合数对答案没影响)

      线性筛质数的时候可以顺便求出每个数的最小质因数,然后就可以做到只枚举i的质因数了。

      时间复杂度O(n*sum*loglogsum),(sum为所有兔纸价钱的总和)(复杂度分析参考埃氏筛法= =)

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define ll long long
     5 using namespace std;
     6 int mn[100023],pri[100233],f[100023],cnt,mx;
     7 int a[55];
     8 bool cant[100233];
     9 int i,j,k,n,m,ans;
    10 
    11 inline void prerun(int mx){
    12     for(i=2;i<=mx;i++){
    13         if(!cant[i])pri[++cnt]=i,mn[i]=i;
    14         for(k=i<<1,j=1;j<=cnt&&k<=mx;k=i*pri[++j]){
    15             cant[k]=1,mn[k]=pri[j];
    16             if(!(i%pri[j]))break;
    17         }
    18     }
    19 }
    20 
    21 int ra;char rx;
    22 inline int read(){
    23     rx=getchar(),ra=0;
    24     while(rx<'0'||rx>'9')rx=getchar();
    25     while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra;
    26 }
    27 inline int min(int a,int b){return a<b?a:b;}
    28 
    29 int main(){
    30 //    memset(f,60,(n+1)<<2);f[1]=0;
    31     n=read();
    32     for(i=1;i<=n;i++)mx=max(mx,a[i]=read()),f[1]+=a[i];
    33     prerun(mx);
    34     register int i,j,k,tmp,sm;
    35     for(i=2;i<=mx;i++)f[i]=f[1];ans=f[1];
    36     for(i=2;i<=mx;ans=min(ans,f[i]),i++)
    37         for(tmp=i;tmp>1;f[i]=min(f[i],sm)){
    38     //        printf("  %d    %d
    ",f[i],f[i/mn[tmp]]);
    39             for(sm=f[j=i/mn[tmp]],k=1;k<=n;k++)sm-=a[k]/i*(mn[tmp]-1);
    40             while(mn[tmp]==mn[tmp/mn[tmp]])tmp/=mn[tmp];tmp/=mn[tmp];
    41         }
    42     printf("%d
    ",ans);
    43     return 0;
    44 }
    View Code
  • 相关阅读:
    Android布局优化
    Handler原理
    最少钱币数:
    ASCII码排序
    Fibonacci数
    奇偶数分离
    认识Dom
    js浏览器对象
    返回/设置时间方法与 Math对象
    window.open
  • 原文地址:https://www.cnblogs.com/czllgzmzl/p/5191034.html
Copyright © 2020-2023  润新知