Bzoj3233 [Ahoi2013]找硬币
Submit: 804 Solved: 408
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
25 102
Sample Output
4
HINT
样例解释:共有两只兔纸,价钱分别为25和102。现在小蛇构造1,25,100这样一组硬币序列,那么付第一只兔纸只需要一个面值为25的硬币,第二只兔纸需要一个面值为100的硬币和两个面值为1的硬币,总共两只兔纸需要付4个硬币。这也是所有方案中最少所需要付的硬币数。
1<=N<=50, 1<=ai<=100,000
Source
动态规划 刷表DP 脑洞题
题目令人十分迷茫,看了题解以后忍不住想大呼“还有这种操作”
会这么想的绝对只有我一个人
设$ f[i] $表示把“用面值为i的硬币付不掉的那部分”干掉 (也就是清除“边角料”)的最小花费。
设$ g[i] $表示最多可以使用的面值为i的硬币的数量(因为硬币面值都是倍数关系,所以能用大的肯定先用大的)。
显然这两个是互补的。
那么显然 ans = min{ f[i] + g[i] }
对于每个i,枚举i的质数倍,有转移:
$f[ prime[j] * i ] = min ( f[ prime[j] * i , f[i] + calc(i , k) )$
这个calc(i,k)是将用k不能解决的部分用i解决所需的硬币i的个数。具体统计方式见代码。
注意除法是整除。
那么问题来了,用i去支付的时候,i不能解决的部分是不是因为整除而漏算了呢?并没有,这部分被统计在f[i]里了
会在这个地方转不过弯的绝对只有我一个人
预处理一下质数,就可以愉快地解决了
因为看着和线性筛有些像,于是试着加了57行那个break,然而是错的2333
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 using namespace std; 7 const int N=100010; 8 const int mxn=60; 9 int read(){ 10 int x=0,f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 13 return x*f; 14 } 15 int n,a[mxn]; 16 int mx; 17 int pri[N],cnt=0; 18 bool vis[N]; 19 int f[N]; 20 void init(){ 21 for(int i=2;i<=mx;i++){ 22 if(!vis[i]){ 23 pri[++cnt]=i; 24 } 25 for(int j=1;j<=cnt && pri[j]*i<=mx;i++){ 26 vis[pri[j]*i]=1; 27 if(i%pri[j]==0)break; 28 } 29 } 30 return; 31 } 32 int calc(int x,int mod){ 33 int res=0; 34 if(!mod){ 35 for(int i=1;i<=n;i++) 36 res+=a[i]/x; 37 return res; 38 } 39 for(int i=1;i<=n;i++) 40 res+=a[i]%mod/x; 41 return res; 42 } 43 int main(){ 44 int i,j,k; 45 n=read(); 46 for(i=1;i<=n;i++)a[i]=read(),mx=max(mx,a[i]); 47 init(); 48 sort(a+1,a+n+1); 49 memset(f,0x3f,sizeof f); 50 f[1]=0; 51 int ans=0x3f3f3f3f; 52 for(i=1;i<=mx;i++){ 53 ans=min(ans,f[i]+calc(i,0)); 54 for(j=1;j<=cnt && ((k=pri[j]*i)<=mx);j++){ 55 f[k]=min(f[k],f[i]+calc(i,k)); 56 // printf("i:%d k:%d fi:%d fk:%d\n",i,k,f[i],f[k]); 57 // if(i%pri[j]==0)break; //WA 58 } 59 } 60 printf("%d\n",ans); 61 return 0; 62 }
本文为博主原创文章,转载请注明出处。