BZOJ 3233: [Ahoi2013]找硬币

BZOJ 3233: [Ahoi2013]找硬币

标签(空格分隔): OI-BZOJ OI-DP


Time Limit: 10 Sec
Memory Limit: 64 MB


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####

\(f_i\)表示最大面值为i所需要的最少硬币数

\[f_i=\min\limits_{[j|i]\ \&[(i/j)\in{prime}]}[f[j]-\sum\nolimits_{k=1}^n{a[k]/i}*(i/j-1)] \]

i/j若不为质数则可以加入(其因数*j)到面额中,答案不会更劣
a[k]/i为整除表示第k个兔子需要多少面额为i的硬币。
一个面额为i的硬币可以替换掉i/j个面额为j的硬币,则面额为i的硬币对答案的贡献为-i/j+1
设m=(max ai);
这个方程直接求解复杂度是\(O(m*n*log_2m)\)
可以对\(\sum\nolimits_{k=1}^n{a[k]/i}\)预处理,复杂度\(O(n*m)\)
对i求j很浪费时间,虽然质数是对数级别的,但是因为m比较小,质数较多,直接枚举的话很慢。
可以用i来更新\(i*Prime_1\)\(i*Prime_2\)\(i*Prime_3\)\(i*Prime_4\)
总复杂度\(O(n*m+m*log_2m)\)


Code####

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<bitset>
#include<vector>
#include<time.h>
using namespace std;
#define PA pair<int,int>
int read()
{
 	int s=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-'0';ch=getchar();}
	return s*f;
}
//smile please
int n,a[55],ma;
int di[100005],f[100005];
int p[100005],pr[100005];
int main()
{
 	//freopen("a.txt","r",stdin);
	//freopen("a.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)
	    a[i]=read(),f[1]+=a[i];
	sort(&a[1],&a[n+1]);ma=a[n];
	for(int i=ma;i;i--)
        for(int j=1;j<=n;j++)
            di[i]+=a[j]/i;
    p[1]=1;
    for(int i=2;i<=ma;i++)
        for(int j=i+i;j<=ma;j+=i)
            p[j]=1;
    for(int i=2;i<=ma;i++)
        if(!p[i])
          pr[++pr[0]]=i;
    pr[++pr[0]]=ma+1;
    int ans=f[1],k;
    for(int i=2;i<=ma;i++)f[i]=f[1];
    for(int j=1;j<=ma;j++)
       {for(int i=1,s;(s=pr[i]*j)<=ma;i++)
            f[s]=min(f[s],f[j]-di[s]*(pr[i]-1));
		ans=min(ans,f[j]);
	   }
	printf("%d\n",ans);
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

posted on 2016-03-12 22:09  wuyuhan  阅读(204)  评论(0编辑  收藏  举报

导航