母函数基础

 

关于母函数的基本定义和初步认识参见杭电lcy的课件 (传送门) 另外还有一篇比较好的文章(传送门)。听着貌似不是那么简单的东西,其实很简单,看一下就懂了。

母函数解决的主要是这样一个问题:几组不同的数,每组选取一个数,相加得到一个结果,问可以有哪些结果并且每种结果的组合方案有几种。巧妙地利用了多项式的乘法以及多项式的化简(合并同类项)来解决问题。

下面是几道题目:(源自于HDU的DIY:http://acm.hdu.edu.cn/diy/contest_show.php?cid=9068)

 

Problem A:问一个整数有多少种拆分方法,如:4=4;4=1+3;4=2+2;4=1+1+2;4=1+1+1+1,即4有5种拆分方法

解析:这题最早做的时候用的是DP,是一种比较高效的方法,母函数也可以拿过来解这道题。

#include <cstdio> #include <cstring> using namespace std; int main(){ int n; while(scanf("%d",&n) != EOF){ int c[122],c1[122]; for(int i = 0; i <= n; ++i)c1[i] = 1; for(int t = 2; t <= n; ++t){ memset(c,0,sizeof(c)); for(int i = 0; i <= n; ++i) for(int j = 0; j + i <= n; j += t){ c[i+j] += c1[i]; } for(int i = 0; i <= n; ++i) c1[i] = c[i]; } printf("%d/n",c1[n]); } return 0; } 

 

Problem B:问用289以内的平方数构成所给的数的方案有多少,与上题雷同

#include <cstdio> #include <cstring> using namespace std; int main(){ int n; while(scanf("%d",&n) != EOF){ if(n == 0)break; int c[310],c1[310]; for(int i = 0; i <= n; ++i)c1[i] = 1; for(int t = 2; t*t <= n && t <= 17; ++t){ memset(c,0,sizeof(c)); for(int i = 0; i <= n; ++i) for(int j = 0; j + i <= n; j += t*t){ c[i+j] += c1[i]; } for(int i = 0; i <= n; ++i) c1[i] = c[i]; } printf("%d/n",c1[n]); } return 0; } 

 

 

Problem C:有1 2 5三种硬币,给你每种硬币的个数,问最小的不能得到的钱数是多少

解析:如对面值为2的硬币,个数有n个,则其所对应的多项式为(1+x^2+x^3+.....+x^(2*n))

 

 

#include <cstdio> #include <cstring> using namespace std; int v[3] = {1,2,5}; int main(){ int a[3]; while(scanf("%d%d%d",&a[0],&a[1],&a[2]) != EOF){ if(a[0] == 0 && a[1] == 0 && a[2] == 0)break; int n = 8001; int c[8001],c1[8001]; memset(c1,0,sizeof(c1)); for(int i = 0; i <= a[0]; ++i)c1[i] = 1; for(int t = 1; t < 3; ++t){ memset(c,0,sizeof(c)); for(int i = 0; i <= n; ++i) for(int j = 0; j +i <= n && j <= a[t]*v[t]; j += v[t]) c[i+j] += c1[i]; for(int i = 0; i <= n; ++i)c1[i] = c[i]; } int ans = 0; while(c1[ans] != 0)ans++; printf("%d/n",ans); } return 0; } 

 

 

另外,这题用多重背包做更快更好

#include <cstdio> #include <cstring> using namespace std; int V[3] = {1,2,5}; int main(){ int a[3]; while(scanf("%d%d%d",&a[0],&a[1],&a[2]) != EOF){ if(a[0] == 0 && a[1] == 0 && a[2] == 0)break; int f[8002],dp[8002]; memset(f,0,sizeof(f)); f[0] = 1; //printf("f = %d/n",f[10000]); for(int i = 0; i < 3; ++i){ memset(dp,0,sizeof(dp)); for(int v = V[i]; v <= 8000; ++v){ if(!f[v] && f[v-V[i]] && dp[v-V[i]] < a[i]){ f[v] = 1; dp[v] = dp[v-V[i]] + 1; } } } //printf("f = %d/n",f[10000]); int ans = 0; while(f[ans])ans++; printf("%d/n",ans); } return 0; } 
PROBLEM D:石子分堆问题,若干已知重量的石头,尽可能的平分 这题本来就是一个背包,母函数也可以解决
#include <cstdio> #include <cstring> using namespace std; bool s[250010]; int main () { int sum,n,a[101],b[101]; int i,j,k; s[0]=1; while (scanf("%d",&n) != EOF){ if (n < 0) break; sum = 0; for (i = 0;i < n;++i){ scanf("%d %d",&a[i],&b[i]); sum += a[i]*b[i]; } for (i = 1;i <= sum/2;++ i) s[i]=0; for (i = 0;i < n;++i){ while (b[i] --){ for (j = sum/2-a[i];j >= 0;-- j){ if (s[j]) s[j+a[i]] = 1; } } } i = sum/2; while (!s[i]) i--; printf ("%d %d/n",sum-i,i); } return 0; } 
Problem E:求水果拼盘的方案数 要求水果的数量是a到b 那么其所对应的多项式为(x^a+..+x^b)
#include <cstdio> #include <cstring> using namespace std; int N,M; int main(){ while(scanf("%d%d",&N,&M) != EOF){ int c[110],c1[110]; memset(c,0,sizeof(c)); int a,b; scanf("%d%d",&a,&b); for(int i = a; i <= b; ++i)c[i] = 1; for(int i = 1; i < N; ++i){ memset(c1,0,sizeof(c1)); scanf("%d%d",&a,&b); for(int k = 0; k <= M; ++k) for(int j = a; j + i <= M && j <= b; ++j) c1[k+j] += c[k]; for(int j = 0; j <= M; ++j)c[j] = c1[j]; } printf("%d/n",c[M]); } return 0; }  
Problem F: 有一个天平(不带游标) 几个已知重量的砝码 问是否可以测量【1,s】内的重量 由于砝码不是只能放在一边的,所以用母函数的时候要加上负幂的情况,而解决负幂的一种方法就是将每一项的指数都加上s,这样就保证没有负幂了。最后结果判断的时候再减去s即可
#include <cstdio> #include <cstring> using namespace std; int c[20010],c1[20010]; int wei[110],N; int main(){ while(scanf("%d",&N) != EOF){ int s = 0; for(int i = 0; i < N; ++i){ scanf("%d",&wei[i]); s += wei[i]; } for(int i = 0; i <= 2*s; ++i)c[i] = 0; c[s] = 1;c[wei[0]+s] = 1;c[-wei[0]+s]= 1; for(int i = 1;i < N; ++i){ for(int t = 0; t <= 2*s; ++t)c1[t] = 0; for(int j = 0; j <= 2*s; ++j){ c1[j] += c[j]; c1[j+wei[i]] += c[j]; c1[j-wei[i]] += c[j]; } for(int t = 0; t <= 2*s; ++t)c[t] = c1[t]; } int cnt = 0,ans[s+2]; for(int i = 0; i <= s; ++i){ if(c[i+s] == 0){ ans[cnt++] = i; } } printf("%d/n",cnt); for(int i = 0; i < cnt - 1; ++i)printf("%d ",ans[i]); if(cnt > 0)printf("%d/n",ans[cnt-1]); } return 0; } 

 

 

你可能感兴趣的:(c)