题意
小蛇是金融部部长。最近她决定制造一系列新的货币。假设她要制造的货币的面值为x1,x2,x3… 那么x1必须为1,xb必须为xa的正整数倍(b>a)。例如 1,5,125,250就是一组合法的硬币序列,而1,5,100,125就不是。不知从哪一天开始,可爱的蛇爱上了一种萌物——兔纸!从此,小蛇便走上了遇上兔纸娃娃就买的不归路。某天,小蛇看到了N只可爱的兔纸,假设这N 只兔纸的价钱分别是a1,a2…aN。现在小蛇想知道,在哪一组合法的硬币序列下,买这N只兔纸所需要的硬币数最少。买兔纸时不能找零。
题解
很妙的题啊
想了好一会毫无头绪。。完全没有方向啊
膜了题解
考虑一个条件,那就是所有的钱币肯定是按倍数出现的
所以如果我们从大到小制造钱币
当前最小的面值是
x
x
那么所有的剩下的数目一定是
a[i]modx
a
[
i
]
m
o
d
x
比x更大的面值对后面的决策是没有任何影响的
并且策略肯定是每一次用大的,用到不能用为止
于是我们可以DP
f[i]
f
[
i
]
表示最小的钱币为
i
i
,那么也就是都变成
a[i]modx
a
[
i
]
m
o
d
x
了,的最小花费
可以得到递推式
f[i]=min(f[j]+(a[i]modj)/i)
f
[
i
]
=
m
i
n
(
f
[
j
]
+
(
a
[
i
]
m
o
d
j
)
/
i
)
其中j是i的倍数
但这样复杂度不够优秀
我们考虑到,如果我们
j/i
j
/
i
,不是一个质数的话,那么我们是可以在里面加上别的数的,反正钱币多一点,又没有关系,最多不用就好了
所以,我们每一次只需要枚举质因数转移就可以了
至于枚举质因数,可以用线性筛优化
CODE:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=55;
const int T=100005;
int n;
int a[N];
int low[T];
int pri[T],tot;
bool ok[T];
void prepare ()
{
memset(ok,true,sizeof(ok));
low[1]=1;
for (int u=2;u<=100000;u++)
{
if (ok[u]) {low[u]=u;pri[++tot]=u;}
for (int i=1;i<=tot;i++)
{
int j=pri[i];
if (j*u>=T) break;
ok[j*u]=false;
low[j*u]=j;
if (u%j==0) break;
}
}
}
int f[T];
int main()
{
prepare();
scanf("%d",&n);
int mx=0;
for (int u=1;u<=n;u++)
{
scanf("%d",&a[u]);
mx=max(mx,a[u]);
}
memset(f,127,sizeof(f));
for (int u=mx;u>=1;u--)
{
int lalal=0;
for (int i=1;i<=n;i++) lalal=lalal+a[i]/u;
f[u]=min(f[u],lalal);
int i=u;
while (i!=1)
{
int j=low[i];
int xx=u/j;
lalal=0;
for (int k=1;k<=n;k++) lalal=lalal+(a[k]%u)/xx;
f[xx]=min(f[xx],f[u]+lalal);
while (i%j==0) i/=j;
}
}
printf("%d\n",f[1]);
return 0;
}