母函数解题和拓展母函数
原题:给定硬币1 5 10 25 50,数目不限,问构成总数n有多少种方法?
很简单,这是一个普通的母函数类型,通解。代码如下:
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 #define mem0(f) memset(f,0,sizeof(f)) 5 #define M 300 6 int c1[M],c2[M]; 7 int main() 8 { 9 int n; 10 int m[5]={1,5,10,25,50}; 11 while(cin>>n) 12 { 13 if(n)cout<<1<<endl; 14 else 15 { 16 mem0(c1); 17 mem0(c2); 18 for(int i=0;i<=n;i++) 19 c1[i]=1; 20 for(int i=1;i<5;i++)//表示括号 21 { 22 for(int k=0;k<=n;k++)//大括号的数据 23 { 24 for(int j=0;j+k<=n;j+=m[i]) 25 { 26 c2[j+k]+=c1[k]; 27 } 28 } 29 for(int j=0;j<=n;j++) 30 { 31 c1[j]=c2[j]; 32 } 33 mem0(c2); 34 } 35 cout<<c1[n]<<endl; 36 } 37 } 38 return 0; 39 }
变体:
题意,硬币1 5 10 25 50,在使用的总硬币数不大于100的情况下构成总数n有多少种方法?
解析:这个题目数据特殊,可以打表做,dp也是通法,这里只介绍母函数法。
母函数的一般思路,一个c1[i]数组,表示构成总数i有c1[i]种方法,另一个c2数组作为中间存储数组,核心思路是一个个括号展开,
在这里为了表示钱币数,数组拓展表示为c1[i][k]表示用k个硬币构成总数i有c1[i][k]种方法。代码如下
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 #define mem0(f) memset(f,0,sizeof(f)) 5 #define M 300 6 int c1[M],c2[M]; 7 int ctz1[M][M],ctz2[M][M];//ctz[i][k]表示用k个银币组成总数i的可能方法书为ctz[i][k] 8 int main() 9 { 10 int n; 11 int m[5]={1,5,10,25,50}; 12 while(cin>>n) 13 { 14 if(!n)cout<<1<<endl; 15 else 16 { 17 mem0(ctz1); 18 mem0(ctz2); 19 mem0(c1); 20 mem0(c2); 21 //for(int i=0;i<=n;i++) 22 //c1[i]=1; 23 //ctz1[0][0]=1; 24 for(int i=0;i<=n&&i<=100;i++) 25 { 26 ctz1[i][i]=1; 27 } 28 for(int i=1;i<5;i++)//表示括号 29 { 30 for(int k=0;k<=n;k++)//大括号的数据 31 { 32 for(int j=0;j*m[i]+k<=n;j++) 33 { 34 //c2[j+k]+=c1[k]; 35 for(int p=0;p+j<=100;p++) 36 { 37 ctz2[j*m[i]+k][p+j]+=ctz1[k][p]; 38 } 39 } 40 } 41 for(int j=0;j<=n;j++) 42 { 43 for(int p=0;p<=100;p++) 44 { 45 ctz1[j][p]=ctz2[j][p]; 46 } 47 //c1[j]=c2[j]; 48 } 49 // mem0(c2); 50 mem0(ctz2); 51 } 52 int s=0; 53 for(int i=0;i<=100;i++) 54 { 55 s+=ctz1[n][i]; 56 } 57 cout<<s<<endl; 58 //cout<<c1[n]<<endl; 59 } 60 } 61 return 0; 62 }
熟练之后,代码就可以精简了:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 #define M 300 6 #define mem0(f) memset(f,0,sizeof(f)) 7 int coin[5]={1,5,10,25,50}; 8 int c1[M][M],c2[M][M];//c1[i][k]表示用k个硬币组成总数i共有c1[i][k] 9 int s; 10 int main() 11 { 12 int n; 13 while(~scanf("%d",&n)) 14 { 15 mem0(c1); 16 mem0(c2); 17 s=0; 18 //初始化,不要忘记了 19 for(int i=0;i<=100;i++) 20 c1[i][i]=1; 21 for(int i=1;i<=4;i++)//还有四个括号要合并 22 { 23 for(int k=0;k<=n;k++)//已经展开了的式子 24 for(int p=0;p*coin[i]+k<=n;p++) 25 { 26 for(int j=0;j+p<=100;j++) 27 { 28 c2[p*coin[i]+k][j+p]+=c1[k][j]; 29 } 30 } 31 for(int i=0;i<=n;i++) 32 { 33 for(int k=0;k<=100;k++) 34 c1[i][k]=c2[i][k]; 35 } 36 mem0(c2); 37 } 38 for(int i=0;i<=100;i++) 39 { 40 s+=c1[n][i]; 41 } 42 printf("%d\n",s); 43 } 44 return 0; 45 }
这样母函数的使用范围又多了一些。
以后就是教我们找拼钱的最小数也不怕了
依然母函数法,找ctz[n][i]for i=0开始找ctz[n][i]非0的i。
通解万岁!
posted on 2013-07-21 11:27 plank george 阅读(154) 评论(0) 编辑 收藏 举报