关于什么是母函数以及母函数的模板什么的,大家可以去找杭电关于母函数的一个课件,还有一位大神的详解,附上网址: http://www.cnblogs.com/freewater/archive/2012/09/11/2679734.html
在此为入门四个题附上解题报告,仅供参考:
现在以每种种类个数无限为例,给出模板::
1 #include <iostream> 2 using namespace std; 3 // Author: Tanky Woo 4 // www.wutianqi.com 5 const int _max = 10001; 6 // c1是保存各项质量砝码可以组合的数目 7 // c2是中间量,保存每一次的情况 8 int c1[_max], c2[_max]; 9 int main() 10 { //int n,i,j,k; 11 int nNum; // 12 int i, j, k; 13 14 while(cin >> nNum) 15 { 16 for(i=0; i<=nNum; ++i) // ---- ① 17 { 18 c1[i] = 1; 19 c2[i] = 0; 20 } 21 for(i=2; i<=nNum; ++i) // ----- ② 22 { 23 24 for(j=0; j<=nNum; ++j) // ----- ③ 25 for(k=0; k+j<=nNum; k+=i) // ---- ④ 26 { 27 c2[j+k] += c1[j]; 28 } 29 for(j=0; j<=nNum; ++j) // ---- ⑤ 30 { 31 c1[j] = c2[j]; 32 c2[j] = 0; 33 } 34 } 35 cout << c1[nNum] << endl; 36 } 37 return 0; 38 }
① 、首先对c1初始化,由第一个表达式(1+x+x^2+..x^n)初始化,把质量从0到n的所有砝码都初始化为1.
② 、 i从2到n遍历,这里i就是指第i个表达式,上面给出的第二种母函数关系式里,每一个括号括起来的就是一个表达式。
③、j 从0到n遍历,这里j就是(前面i個表达式累乘的表达式)里第j个变量,(这里感谢一下seagg朋友给我指出的错误,大家可以看下留言处的讨论)。如(1+x)(1+x^2)(1+x^3),j先指示的是1和x的系数,i=2执行完之后变为
(1+x+x^2+x^3)(1+x^3),这时候j应该指示的是合并后的第一个括号的四个变量的系数。
④ 、 k表示的是第j个指数,所以k每次增i(因为第i个表达式的增量是i)。
⑤ 、把c2的值赋给c1,而把c2初始化为0,因为c2每次是从一个表达式中开始的。
1. 题目:http://acm.hdu.edu.cn/showproblem.php?pid=1028
题目大意不用我说了吧?太简单了,理解了模板这题就A了
1 #include <iostream> 2 #include<stdio.h> 3 using namespace std; 4 const int _max = 300; 5 int c1[_max], c2[_max]; 6 int main() 7 { 8 int n; 9 int i, j, k; 10 while(scanf("%d",&n)!=EOF) 11 { 12 for(i=0; i<=n; ++i) 13 { 14 c1[i] = 1; 15 c2[i] = 0; 16 } 17 for(i=2; i<=n; ++i) 18 { 19 20 for(j=0; j<=n; ++j) 21 for(k=0; k+j<=n; k+=i) 22 { 23 c2[j+k] += c1[j]; 24 } 25 for(j=0; j<=n; ++j) 26 { 27 c1[j] = c2[j]; 28 c2[j] = 0; 29 } 30 } 31 printf("%d ",c1[n]); 32 } 33 return 0; 34 }
2.题目:http://acm.hdu.edu.cn/showproblem.php?pid=1398
题目大意:
硬币的面值分别为1,4,9,16.....17^2,输入n,问最多有几种组成方式。
由题意我们可以写出母函数表达式:(1+x^1+x^2+x^3.....)*(1+x^4+x^8+x^12.....)*(1+x^9+x^18+......).....
由该表达式,我们只需改动原模板的几个地方即可,具体看代码:
1 #include <iostream> 2 #include<stdio.h> 3 #define maxn 301 4 using namespace std; 5 int c1[maxn],c2[maxn]; 6 int main() 7 { 8 int n,i,j,k; 9 while(scanf("%d",&n)&& n!=0) 10 { 11 for (i=0; i<=n; ++i) 12 { 13 c1[i]=1; 14 c2[i]=0; 15 } 16 for (i=2; i<18; ++i)//有17组表达式 17 { 18 for (j=0; j<=n; ++j) 19 for (k=0; k+j<=n; k+=i*i)//每次加i*i 20 { 21 c2[j+k]+=c1[j]; 22 } 23 for(j=0; j<=n; ++j) 24 { 25 c1[j]=c2[j]; 26 c2[j]=0; 27 } 28 } 29 printf("%d ",c1[n]); 30 } 31 return 0; 32 }
3. 题目:http://acm.hdu.edu.cn/showproblem.php?pid=1085
这道题终于有了一些变化,仔细分析也不难,给你面值为1,2,5的硬币的个数,问最小的不能由这些硬币组成的价值。
1 #include<stdio.h> 2 #include<string.h> 3 #define maxn 10001 4 int c1[maxn],c2[maxn]; 5 int main() 6 { 7 int w[4]={0,1,2,5};//存面额 8 int num[4];//存相应面额的个数 9 int i,j,k; 10 int sum,ksum; 11 while(scanf("%d%d%d",&num[1],&num[2],&num[3])!=EOF) 12 { 13 if(num[1]==0&&num[2]==0&&num[3]==0) 14 break; 15 sum=num[1]+num[2]*2+num[3]*5;//最大能组成的面额数 16 memset(c1,0,sizeof(c1)); 17 memset(c2,0,sizeof(c2)); 18 for(i=0; i<=w[1]*num[1]; i+=w[1]) 19 c1[i]=1;//初始化 20 ksum=w[1]*num[1]; 21 for(i=2; i<=3; ++i) 22 { 23 for(j=0; j<=ksum; ++j) 24 for(k=0; k<=num[i]*w[i]; k+=w[i])//注意此处 25 c2[k+j]=c1[j]; 26 for(j=0; j<=ksum+w[i]*num[i]; ++j) 27 { 28 c1[j]=c2[j]; 29 c2[j]=0; 30 } 31 ksum+=w[i]*num[i]; 32 } 33 for(i=0; i<=sum; i++) 34 if(c1[i]==0) 35 { 36 printf("%d ",i); 37 break; 38 } 39 if(i==sum+1) 40 printf("%d ",sum+1); 41 } 42 return 0; 43 }
万变不离其宗,请大家结合代码好好分析。
4. 题目:http://acm.hdu.edu.cn/showproblem.php?pid=1171
和第三题相似,最后要求输出的是分成两份的钱相等的情况,如果不相等,使差值尽可能小
1 #include<stdio.h> 2 #include<string.h> 3 #define maxn 3000000 4 int c1[maxn],c2[maxn],num[55],w[55]; 5 int main() 6 { 7 int n,i,j,k; 8 int sum,ksum; 9 while(scanf("%d",&n)!=EOF) 10 { 11 sum=0; 12 if(n<0) 13 break; 14 memset(c1,0,sizeof(c1)); 15 memset(c2,0,sizeof(c2)); 16 for(i=1; i<=n; i++) 17 { 18 scanf("%d%d",&w[i],&num[i]); 19 sum+=w[i]*num[i]; 20 } 21 for(i=0; i<=w[1]*num[1]; i+=w[1]) 22 c1[i]=1; 23 ksum=w[1]*num[1]; 24 for(i=2; i<=n; ++i) 25 { 26 for(j=0; j<=ksum; ++j) 27 for(k=0; k<=num[i]*w[i]; k+=w[i]) 28 c2[k+j]=c1[j]; 29 for(j=0; j<=ksum+w[i]*num[i]; ++j) 30 { 31 c1[j]=c2[j]; 32 c2[j]=0; 33 } 34 ksum+=w[i]*num[i]; 35 } 36 for(i=sum/2; i>=0; --i) 37 if(c1[i]!= 0) 38 { 39 printf("%d %d ", sum-i, i); 40 break; 41 } 42 } 43 return 0; 44 }
四道题目解完了,是不是很简单呢?
推荐做题:HDOJ:1709,1028、1709、1085、1171、1398、2069、2152。