1. 程式人生 > >找硬幣

找硬幣

oid eof 組合 技術分享 sizeof font aac () 很多

時間限制: 1 Sec 內存限制: 64 MB

題目描述

小蛇是金融部部長。最近她決定制造一系列新的貨幣。假設她要制造的貨幣的面值為x1,x2,x3… 那麽x1必須為1,xb必須為xa的正整數倍(b>a)。例如 1,5,125,250就是一組合法的硬幣序列,而1,5,100,125就不是。不知從哪一天開始,可愛的蛇愛上了一種萌物——兔紙!從此,小蛇便走上了遇上兔紙娃娃就買的不歸路。某天,小蛇看到了N只可愛的兔紙,假設這N 只兔紙的價錢分別是a1,a2…aN。現在小蛇想知道,在哪一組合法的硬幣序列下,買這N只兔紙所需要的硬幣數最少。買兔紙時不能找零。

輸入

第一行,一個整數N,表示兔紙的個數

第二行,N個用空格隔開的整數,分別為N只兔紙的價錢

輸出

一行,一個整數,表示最少付的錢幣數。

樣例輸入

2 
25 102 

樣例輸出

4

提示

樣例解釋:共有兩只兔紙,價錢分別為25和102。現在小蛇構造1,25,100這樣一組硬幣序列,那麽付第一只兔紙只需要一個面值為25的硬幣,第二只兔紙需要一個面值為100的硬幣和兩個面值為1的硬幣,總共兩只兔紙需要付4個硬幣。這也是所有方案中最少所需要付的硬幣數。

1<=N<=50, 1<=ai<=100,000

題解

雖然說是T1但是本著不會做就跳過的原則我還是果斷把它忽略了……事實證明T3極其水,後來回來給這道題打了個dfs還有28分。剛開始以為是每一層的倍數都必須相同,然後自己在那裏枚舉得還挺high,後來發現不是那麽回事,迷之尷尬。因為一貫是讀完三道題,各自稍微想一下再開始按自己思路的有無集中做每一道題,所以有很多時候做完別的題再回來就對題意的理解出了偏差,下一次應該只要開始做就再讀一遍題。

有一個結論是每一層的倍數都應該枚舉素數。因為發行硬幣的種類是不限的,所以每一個合數都可以由很多素數的乘積得到。想象一下枚舉第一個素數p1,那麽a[i]%p1是一定要用一元硬幣去買的。下一層再枚舉一個素數p2,(a[i]-a[i]%p1)%(p1*p2)一定是要用p1去買的,這樣每一層枚舉之後把a[i]/=p,下一層就仍可以按這樣的方式枚舉,相當於秦九韶算法的逆運算,ans即為余數之和,深搜時可以比較當前的summod和ans來及時停止無意義的枚舉。有一個我剛開始沒註意到的優化是如果p已經大於當前最大的a[i],再枚舉p是沒有用的,直接把summod+=sigma(a[i])然後更新答案就可以停止當前dfs了。

技術分享
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int sj=100005;
int n,a[55],tp,z,jd,p[sj],ge;
long long ans;
bool nprime[sj];
void dfs(int x,int st)
{
     if(x>=ans) return;
     int b[55];
     memcpy(b,a,sizeof(b));
     for(int i=1;i<=ge;i++)
     {
       tp=x;
       if(a[n]<p[i]) 
       {
          for(int j=st;j<=n;j++)
            tp+=a[j];
          if(tp<ans) ans=tp;
          break;
       }
       for(int j=st;j<=n;j++)
       {
         z=a[j]/p[i];
         tp+=a[j]-z*p[i];
         a[j]=z;
         if(tp>=ans) break;
       }
       if(tp<ans)
         for(int j=st;j<=n;j++)
         {
            if(a[j]!=0) 
            {
               dfs(tp,j);
               break;
            }
            if(j==n)  ans=tp;
         }
       memcpy(a,b,sizeof(a));
     }   
}
void solve(int x)
{
     nprime[0]=nprime[1]=1;
     for(int i=2;i<=x;i++)
     {
       if(!nprime[i])
         p[++ge]=i;
       for(int j=1;j<=ge&&i*p[j]<=x;j++)
       {
         nprime[i*p[j]]=1;
         if(!(i%p[j])) break;
       }
     }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
      scanf("%d",&a[i]);
      if(jd<a[i])  jd=a[i];
      ans+=a[i];
    }
    solve(jd);
    sort(a+1,a+n+1,less<int>());
    dfs(0,1);
    printf("%lld",ans);
    return 0;
}
coin

找硬幣