母函数及其应用
一、母函数的定义
对于序列a0,a1,a2… 构造一函数:
G(x) = a0 + a1x + a2x^2 + …,
称函数G(x)是序列a0,a1,a2,…的母函数。
如果已知函数:G(x) = (1+x)^2
如果我们可以生成一个序列1,2,1,所以:G(x)是1,2,1的母函数。
问题引入:
若有1,2,3,4克砝码各一枚,能称出哪几种重量?各有几种可能方案?
构造母函数:用x的支书表示出称出的重量,则
1个1g的砝码可以用函数1+x表示;
1个2g的砝码可以用函数1+x^2表示;2个2g用函数1+x^2+x^4
1个3g的砝码可以用函数1+x^3表示;
1个4g的砝码可以用函数1+x^4表示;
(1+x)*(1+x^2)*(1+x^3)*(1+x^4) = 1+x+x^2+2x^3+2x^4…
系数是方案数,而指数是重量。
3个1g的砝码,1+x+x^2+x^3
概念:整数拆分,就是把整数拆分成若干整数的和。
拆分成若干整数的和,办法不已,不同拆分法的总数叫做拆分数。
二、普通型母函数-模板(整数拆分为例)
练习:整数n拆分成1,2,3,4,…,m的和,求母函数。
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <bits/stdc++.h>
using namespace std;
const int lmax = 10000;
int c1[lmax+1],c2[lmax+1];
// 母函数 g = (1+x+x^2+...)*(1+x^2+x^4+...)(1+x^3+x^6+...)...
int main()
{
int n,i,j,k;
while(cin>>n)
{
for(i=0; i<=n; i++) // 初始化
{
c1[i]=0;
c2[i]=0;
}
for(i=0; i<=n; i++) //初始化第一个小括号里面的表达式系数,下标表示指数
c1[i]=1;
for(i=2; i<=n; i++) // 控制进行几次,两对括号的合并。几个括号一般确定
{
for(j=0; j<=n; j++) //第一个括号
{ // 下面的k的变化可以用数组使其加一定的值给于变化。
for(k=0; k+j<=n; k+=i)//i表示第二个括号指数的增值(没有表示)
// c2表示两个括号称的结果,
{
c2[j+k]+=c1[j]*1; // 因为第二个数组的系数是1,所以*1
}
}
for(j=0; j<=n; j++)
{
c1[j]=c2[j];
c2[j]=0;
}
}
cout << c1[n] << endl;
}
return 0;
}
三、例题
1、Square Coins
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
using namespace std;
int a[401],b[401];
int main()
{
int n,i,j,k,l,summax;
while(~scanf("%d",&n)&&n)
{
for(i=0;i<=300;i++)
{
a[i]=1;
b[i]=0;
}
for(i=2;i<=17;i++)
{
for(j=0;j<=n;j++)
{
for(k=0;k<=n-j;k+=i*i)
b[k+j]+=a[j];
}
for(j=0;j<=n;j++)
{
a[j]=b[j];
b[j]=0;
}
}
printf("%d\n",a[n]);
}
}
2、Holding Bin-Laden Captive!
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=10000;
int c1[maxn],c2[maxn],t[6];
int main()
{
while(scanf("%d%d%d",&t[1],&t[2],&t[5])){
if(t[1]==0&&t[2]==0&&t[5]==0){
break;
}else{
int sum=t[1]+t[2]*2+t[5]*5;
for(int i=0;i<=sum;i++){
c1[i]=0;
c2[i]=0;
}
for(int i=0;i<=t[1];i++){
c1[i]=1;
}
for(int i=0;i<=t[1];i++){
for(int j=0;j<=t[2]*2;j+=2){
c2[j+i]+=c1[i];
}
}
for(int i=0;i<=t[1]+t[2]*2;i++){
c1[i]=c2[i];
c2[i]=0;
}
for(int i=0;i<=t[1]+t[2]*2;i++){
for(int j=0;j<=t[5]*5;j+=5){
c2[j+i]+=c1[i];
}
}
for(int i=0;i<=t[1]+t[2]*2+t[5]*5;i++){
c1[i]=c2[i];
c2[i]=0;
}
int i;
for(i=0;i<=sum;i++){
if(!c1[i]){
cout<<i<<endl;
break;
}
}
if(i==sum+1){
cout<<i<<endl;
}
}
}
return 0;
}
3、Big Event in HDU
#include<cstdio>
#include<stack>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<iostream>
using namespace std;
int c[250002];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
if(n<0)
break;
int time[55],num[55];
int sum=0;
for(int i=0;i<n;i++)
{
scanf("%d%d",&time[i],&num[i]);
sum+=time[i]*num[i];
}
for(int i=0;i<=sum/2;i++)
{
if(i%time[0])
c[i]=0;
else
c[i]=1;
}
for(int i=0;i<n;i++)
{
for(int j=0;j<=sum/2;j++)
{
for(int k=0;k+j<=sum/2&&k<=time[i]*num[i];k+=time[i])
{
if(c[j]==1)
c[j+k]=1;
}
}
}
int i;
for(i=sum/2;i>=0;i--)
{
if(c[i]!=0)
break;
}
printf("%d %d\n",sum-i,i);
}
}
4、The Balance
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
#define maxn 101
int a[maxn*maxn],b[maxn*maxn],c[maxn];
int main()
{
int n;
while(cin>>n)
{
int sum = 0;
for(int i = 0; i < n; i++)
{
cin>>c[i];
sum+=c[i];
}
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i = 0; i <= c[0]; i+=c[0])
a[i] = 1;
//int now = c[0];
for(int i = 1; i < n; i++) //i代表第i个表达式
{
for(int j = 0; j <= sum; j++) //j表示每个表达式的第i项
{
for(int k = 0; k <= c[i]; k+=c[i]) //k代表每个表达式第i项的指数
{
b[j+k] += a[j];
b[abs(j-k)] +=a[j];
}
}
//now += c[i];
for(int j = 0; j <= sum; j++)
{
a[j] = b[j];
b[j] = 0;
}
}
int num = 0;
for(int i = 0; i <= sum; i++ )
{
if(!a[i])
num++;
}
cout<<num<<endl;
int p = 1;
if(num){
for(int i = 0; i <= sum; i++)
{
if(!a[i])
{
if(!p)
cout<<" "<<i;
else
{
cout<<i;
p = 0;
}
}
}
cout<<endl;
}
}
}
5、fruit
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int mi[105],mx[105],c1[105],c2[105];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;i++)
scanf("%d%d",&mi[i],&mx[i]);
memset(c1,0,sizeof(c1));
for(int i=mi[1];i<=mx[1];i++)
c1[i]=1;
for(int i=2;i<=n;i++)
{
memset(c2,0,sizeof(c2));
for(int j=0;j<=m;j++)
for(int k=mi[i];k<=mx[i]&&k+j<=m;k++)
c2[k+j]+=c1[j];
for(int j=0;j<=m;j++)
c1[j]=c2[j];
}
printf("%d\n",c1[m]);
}
return 0;
}
6、排列组合
有n种物品,并且知道每种物品的数量。要求从中选出m件物品的排列数。例如有两种物品A,B,并且数量都是1,从中选2件物品,则排列有"AB","BA"两种。
#include <iostream>
#include<stdio.h>
#include<memory.h>
using namespace std;
int f(int n)
{
int i,s=1;
for(i=1; i<=n; i++)
s=s*i;
return s;
}
int main()
{
int n,m,i,j,k,num[12];
double c1[12],c2[12];//因为用到了除法,所以用double型来存储。
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));
for(i=0; i<n; i++)
{
cin>>num[i];
}
for(i=0; i<=num[0]; i++)//赋初值
c1[i]=1.0/f(i)
for(i=1; i<n; i++)//n个括号
{
for(j=0; j<=n; j++)
for(k=0; k<=num[i]&&k+j<=n; k++)
c2[k+j]+=c1[j]/f(k);
for(j=0; j<=n; j++)
{
c1[j]=c2[j];
c2[j]=0;
}
}
printf("%.lf\n",c1[m]*f(m));
}
return 0;
}