【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 只兔子所需要的硬币数最少。 买兔子时不能找零。
【输入格式】
第一行, 一个整数 N, 表示兔子的个数 第二行, N 个用空格隔开的整数, 分别为 N 只兔子的价钱
【输出格式】
一行, 一个整数, 表示最少付的钱币数。
【样例输入】
2
25 102
【样例输出】
4
【样例解释】
本次比赛试题由厦门一中提供 共有两只兔子, 价钱分别为 25 和 102。 现在小蛇构造 1, 25, 100 这样一组 硬币序列, 那么付第一只兔子只需要一个面值为 25 的硬币, 第二只兔子需要一 个面值为 100 的硬币和两个面值为 1 的硬币, 总共两只兔子需要付 4 个硬币。 这 也是所有方案中最少所需要付的硬币数。
【数据范围与约定】
对于 20%的数据, 1<=N<=20, 1<=ai<=5, 000
对于 100%的数据, 1<=N<=50, 1<=ai<=100, 000 部分数据随机.
一道不错的动态规划,考试的时候完全完全没有思路呢。
先考虑DP数组 f[i] 表示此时硬币面额的最大值为i时,买所有小兔纸所用的最少硬币,显然边界的 f[1]=∑1n ai
然后是转移。
f[i*j]=min(f[i*j],f[i]-ts[i*j]*(j-1)) (SMG??)
首先i和j都是数字i*j的因子咯,所以一个面值为i*j的硬币相当于j个面值为i的硬币,即j个i硬币可以变成 1 个i*j硬币,所以硬币数量就会减少 (j-1) 个啦。
然后ts数组里存的是i*j这个硬币最多可以用几次,就是 Sigma{a[k]/(i-j)} (打Sigma好辛苦)
具体看代码吧(并没有很长)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 7 #define For(i,a,b) for(register int i=a;i<=b;++i) 8 #define Re register 9 #define inf 0x7f7f7f 10 using namespace std; 11 const int N=55,MN=1e5+10; 12 int a[N],ts[MN],f[MN]; 13 int n,mx=0,fn; 14 inline void read(int &v){ 15 v=0; 16 char c=getchar(); 17 while(c<'0'||c>'9')c=getchar(); 18 while(c>='0'&&c<='9')v=v*10+c-'0',c=getchar(); 19 } 20 int main(){ 21 // freopen("coin.in","r",stdin); 22 // freopen("coin.out","w",stdout); 23 read(n); 24 memset(f,inf,sizeof(f)); 25 For(i,1,n) read(a[i]),mx=max(a[i],mx); 26 For(i,1,mx) For(j,1,n) ts[i]+=a[j]/i; 27 fn=ts[1]; f[1]=ts[1]; 28 For(i,1,mx){ 29 fn=min(fn,f[i]); 30 for(Re int j=1;j*i<=mx;++j){ 31 f[i*j]=min(f[i*j],f[i]-ts[i*j]*(j-1)); 32 } 33 } 34 cout<<fn<<endl; 35 return 0; 36 }