找硬幣
時間限制: 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
找硬幣