bzoj 3233: [Ahoi2013]找硬币

题意

小蛇是金融部部长。最近她决定制造一系列新的货币。假设她要制造的货币的面值为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]剩下的数目一定是 a[i]modx a [ i ] m o d x
比x更大的面值对后面的决策是没有任何影响的
并且策略肯定是每一次用大的,用到不能用为止
于是我们可以DP
f[i] f [ i ] 表示最小的钱币为 i i ,那么也就是a[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值