BZOJ 3233 Ahoi2013 找硬币 动态规划

题目大意:给定 n 个数,求一种混合进制使得每个数各个位之和之和最小
fi 表示表示最大硬币面值为 i 时零头部分(即 ak mod i 部分)的最小硬币数
那么有转移方程: fj=min{fi+nk=1ak mod ji}(i|j)
然后 ans=min{fi+nk=1aki}
时间复杂度 O(nmlogm) ,光荣TLE
优化: ji 一定是质数,否则我可以多添加一种硬币而不伤身体(雾
那么我们只需要枚举质数倍数即可
时间复杂度 O(nmloglogm) ,这样就可以过了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;
int n,a[55];
int f[M],ans=0x3f3f3f3f;
int prime[M],tot;
bool not_prime[M];
void Linear_Shaker()
{
    int i,j;
    for(i=2;i<=100000;i++)
    {
        if(!not_prime[i])
            prime[++tot]=i;
        for(j=1;prime[j]*i<=100000;j++)
        {
            not_prime[prime[j]*i]=true;
            if(i%prime[j]==0)
                break;
        }
    }
}
int main()
{
    int i,j,k;
    Linear_Shaker();
    cin>>n;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    memset(f,0x3f,sizeof f);f[1]=0;
    for(i=1;i<=100000;i++)
    {
        for(j=1;j<=tot&&prime[j]*i<=100000;j++)
        {
            int val=prime[j]*i;
            long long temp=0;
            for(k=1;k<=n;k++)
                temp+=a[k]%val/i;
            if(temp<=0x3f3f3f3f)
                f[val]=min(f[val],f[i]+(int)temp);
        }
        long long temp=f[i];
        for(k=1;k<=n;k++)
            temp+=a[k]/i;
        if(temp<=0x3f3f3f3f)
            ans=min(ans,(int)temp);
    }
    cout<<ans<<endl;
    return 0;
}

你可能感兴趣的:(动态规划,bzoj,BZOJ3233)