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

 #include<iostream>
#include<cstring>
#include<cstdio>
#define N (100009)
using namespace std; int n,cnt,maxn,a[N],prime[N],d[N],f[N]; void Euler()
{
for (int i=; i<=maxn; ++i)
{
if (!d[i]) {prime[++cnt]=i; d[i]=i;}
for (int j=; j<=cnt && i*prime[j]<=maxn; ++j)
{
d[i*prime[j]]=prime[j];
if (i%prime[j]==) break;
}
}
} int main()
{
scanf("%d",&n);
for (int i=; i<=n; ++i)
scanf("%d",&a[i]), maxn=max(maxn,a[i]);
Euler();
memset(f,0x7f,sizeof(f));
for (int i=maxn; i>=; --i)
{
int sum=;
for (int j=; j<=n; ++j)
sum+=a[j]/i;
f[i]=min(f[i],sum);
int x=i;
while (x>)
{
int p=d[x],sig=;
for (int j=; j<=n; ++j)
sig+=a[j]%i/(i/p);
f[i/p]=min(f[i/p],f[i]+sig);
while (x%p==) x/=p;
}
}
printf("%d\n",f[]);
}
上一篇:一次非常有意思的sql优化经历


下一篇:[Bzoj3233][Ahoi2013]找硬币[基础DP]