BZOJ3233:[AHOI2013]找硬币(DP)
Description
小蛇是金融部部长。最近她决定制造一系列新的货币。假设她要制造的货币的面值为x1,x2,x3… 那么x1必须为1,xb必须为xa的正整数倍(b>a)。例如 1,5,125,250就是一组合法的硬币序列,而1,5,100,125就不是。不知从哪一天开始,可爱的蛇爱上了一种萌物——兔纸!从此,小蛇便走上了遇上兔纸娃娃就买的不归路。某天,小蛇看到了N只可爱的兔纸,假设这N 只兔纸的价钱分别是a1,a2…aN。现在小蛇想知道,在哪一组合法的硬币序列下,买这N只兔纸所需要的硬币数最少。买兔纸时不能找零。
Input
第一行,一个整数N,表示兔纸的个数
第二行,N个用空格隔开的整数,分别为N只兔纸的价钱
Output
一行,一个整数,表示最少付的钱币数。
Sample Input
2
25 102
Sample Output
4
HINT
样例解释:共有两只兔纸,价钱分别为25和102。现在小蛇构造1,25,100这样一组硬币序列,那么付第一只兔纸只需要一个面值为25的硬币,第二只兔纸需要一个面值为100的硬币和两个面值为1的硬币,总共两只兔纸需要付4个硬币。这也是所有方案中最少所需要付的硬币数。
1<=N<=50, 1<=ai<=100,000
Solution
因为选的序列是倍数的原因,所以我们可以得到一个贪心:尽可能选大的。
也就是我们从大到小确定面额,如果当前确定的面额为$x$,那么所有兔子剩下需要付的就是$a[i]\% x$。
设$f[i]$表示当前最小面值为$i$时的最少付钱次数。
初始化$f[i]=\sum_{j=1}^na[j]/i$。
设$p$为$f[i]$的一个质因数
转移$f[i/p]=min(f[i/p],f[i]+\sum_{j=1}^{n} a[j]\% i/(i/p))$
并且你需要一个线筛来快速找出一个数的所有质因数。
为什么这么转移懒得写了,反正就是基于贪心的思想应该并不难理解QAQ
我:为啥这个初始化不上取整啊……难不成钱不够兔子还给你抹个零……
$sugar$:你别说还真的抹个零,因为零头在后面$DP$会付掉的……
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define N (100009) 5 using namespace std; 6 7 int n,cnt,maxn,a[N],prime[N],d[N],f[N]; 8 9 void Euler() 10 { 11 for (int i=2; i<=maxn; ++i) 12 { 13 if (!d[i]) {prime[++cnt]=i; d[i]=i;} 14 for (int j=1; j<=cnt && i*prime[j]<=maxn; ++j) 15 { 16 d[i*prime[j]]=prime[j]; 17 if (i%prime[j]==0) break; 18 } 19 } 20 } 21 22 int main() 23 { 24 scanf("%d",&n); 25 for (int i=1; i<=n; ++i) 26 scanf("%d",&a[i]), maxn=max(maxn,a[i]); 27 Euler(); 28 memset(f,0x7f,sizeof(f)); 29 for (int i=maxn; i>=1; --i) 30 { 31 int sum=0; 32 for (int j=1; j<=n; ++j) 33 sum+=a[j]/i; 34 f[i]=min(f[i],sum); 35 int x=i; 36 while (x>1) 37 { 38 int p=d[x],sig=0; 39 for (int j=1; j<=n; ++j) 40 sig+=a[j]%i/(i/p); 41 f[i/p]=min(f[i/p],f[i]+sig); 42 while (x%p==0) x/=p; 43 } 44 } 45 printf("%d\n",f[1]); 46 }