母函数

定义 (摘自百度百科):

对于任意数列a0,a1,a2...an 即用如下方法与一个函数联系起来:

~G(x) = a0 + a1x + a2x^2 + a3x^3 +....+ anx^n

则称G(x)是数列的母函数。

这个母函数有什么用呢?

我们可以看这样一个例子,假设你有1角钱,2角钱,3角钱,4角钱各一张。要付给一个5角钱的东西,不找零,有几种付款方式呢?

根据枚举,我们不难发现其实有2种付款方式,一种是1角钱+4角钱,另一种是2角钱+3角钱。

下面我们给出另一种奇妙的解题方式:

=>1张1角钱表示成1+x (若有两张,则表示成1+x+x^2);

=>1张2角钱表示成1+x^2 (若有两张,则表示成1+x^2+x^4);

=>1张3角钱表示成1+x^3 (若有两张,则表示成1+x^3+x^6);

=>1张4角钱表示成1+x^4 (若有两张,则表示成1+x^4+x^8);

那么(1+x)*(1+x^2)*(1+x^3)*(1+x^4)等于多少呢?

根据本人亲自手算,这个结果应该是:

1+x+x^2+2*x^3+2*x^4+2*x^5+2*x^6+2*x^7+x^8+x^9+x^10.

可以发现x^4的系数是2,这和我们枚举得到的结果是一样的。

其实要求付一个n角钱的东西有几种付款方式,只需要看x^n的系数就可以了。

回到定义上,我们就会发现(1+x)*(1+x^2)*(1+x^3)*(1+x^4)其实就是母函数的一种形式。

那我们怎么写代码来计算呢?

我以(1+x)*(1+x^2)*(1+x^3)*(1+x^4)为例子:

我们首先需要开两个数组c1[maxn],c2[maxn];第一个数组用来存放每一项的系数,第二个数组是临时存放系数。

例如c1[2]=5,即表示指数为2时,系数为5. 那数组开多大好呢,我们应该发现指数最大应该是1+2+3+4=10.所以开20个足够了!

其实写代码的过程就是一个模拟手算的过程。第一步,应该先计算(1+x)*(1+x^2),结果是1+x+x^2+x^3.

动动膝盖也应该知道,这道题是需要用循环的。

其实写代码的过程就是一个模拟手算的过程。

首先第一个循环变量 i ,表示第i个多项式,我们应该把它从i=2开始循环到4.

for(int i=2;i<=4;i++)

但是,第一个式子是有初值的,1+x,它的c1[0]=1,c2[0]=1.

所以我们需要初始化数组的数据:maxn为数组的大小

for(int i=0;i<maxn;i++)
{
      c1[i]=1;
      c2[i]=0;
}

我们用另一个循环变量 j 来表示i-1个式子中的每一项。

例如本式子中,第一个循环 i=2 时计算(1+x)*(1+x^2),j=0表示1,j=1时表示x。

                            第二个循环 i=3 时计算(1+x+x^2+x^3)*(1+x^3),j=0表示1,j=1时表示x,j=2表示x^2,j=3表示x^3.

我们发现,j 的上限受 i 制约,j 的最大值为i*(i-1)/2;

for(int j=0;j<=0.5*i*(i-1);j++)

i=3 时计算(1+x+x^2+x^3)*(1+x^3),j=0表示1,j=1时表示x,j=2表示x^2,j=3表示x^3.那(1+x^3)的每一项还得与前面的做运算呢。

所以需要另一个循环变量k,我们把k看成指数的增长量。

什么意思呢?就是(1+x^3),x指数分别为0,3。

则:

i=2 时,计算(1+x)*(1+x^2),

             j=0时:1分別与(1+x^2)相乘,

                         k=0,与1相乘;

                         k=2,与x^2相乘。(可以发现k的增量是i,因此代码中k+=i

             j=1时,x分別与(1+x^2)相乘,

                         k=0,与1相乘;

                         k=2,与x^2相乘。(可以发现k的增量是i,因此代码中k+=i)

            算完后c1[0]=1,c1[1]=1,c1[2]=1,c1[3]=1;

i=3时,计算((1+x+x^2+x^3)*(1+x^3)

            j=0时:1分別与(1+x^3)相乘,

                         k=0,与1相乘;

                         k=3,与x^2相乘。(可以发现k的增量是i,因此代码中k+=i)

           ……

……

代码:

//计算(1+x)*(1+x^2)*(1+x^3)*(1+x^4)
#include <iostream>

using namespace std;
#define maxn 20 //数组的大小
int c1[maxn],c2[maxn];

int main()
{
       int m;
       for(int i=0;i<maxn;i++) //初始化第一个式子:(1+x^2+x^3+...)
       {
           c1[i]=1;
           c2[i]=0;
       }
       for(int i=2;i<=4;i++) // i 从第二个式子循环到第四个
       {
           for(int j=0;j<=0.5*i*(i-1);j++) // j 从0循环到它所在式子中的指数最大值 
           {
               for(int k=0,t=0;t<=1;t++,k+=i) //本例子中 k 表示指数增量,t 表示 k 增加几次。本式子中k增加1次
               {
                   c2[k+j]+=c1[j];
               }
           }
           for(int j=0;j<maxn;j++) //更新数组
           {
               c1[j]=c2[j];
               c2[j]=0;
           }
       }
       for(int i=0;i<maxn;i++)
        cout<<c1[i]<<" ";
        cout<<endl;

   return 0;
}

运行后:

1 1 1 2 2 2 2 2 1 1 1 0 0 0 0 0 0 0 0 0

有了这个思路之后,就可以拓宽我们的解题范围了!

我们不再限制是有一张了,而是有很多张。

看下面这道题:

1.     Square Coins

Problem Description
People in Silverland use square coins. Not only they have square shapes but also their values are square numbers. Coins with values of all square numbers up to 289 (=17^2), i.e., 1-credit coins, 4-credit coins, 9-credit coins, ..., and 289-credit coins, are available in Silverland. 
There are four combinations of coins to pay ten credits: 
ten 1-credit coins,
one 4-credit coin and six 1-credit coins,
two 4-credit coins and two 1-credit coins, and
one 9-credit coin and one 1-credit coin. 
Your mission is to count the number of ways to pay a given amount using coins of Silverland.
 Input
The input consists of lines each containing an integer meaning an amount to be paid, followed by a line containing a zero. You may assume that all the amounts are positive and less than 300.
 Output
For each of the given amount, one line containing a single integer representing the number of combinations of coins should be output. No other characters should appear in the output. 
 Sample Input
2
10
30
0
 Sample Output
1
4
27

有无限张1角,4角(2*2),9角(3*3)到289角(17*17)。问要付N角的东西,有几种付款方式?

这个就是求(1+x+x^2+x^3+x^4+···)*(1+x^4+x^8+···)*(1+x^9+x^18+···)···求解之后x的N次幂的系数。

我们改一下三个for循环的范围即可:

代码:

#include <iostream>

using namespace std;
#define maxn 305
int c1[maxn],c2[maxn];

int main()
{
   int m;
   while(cin>>m&&m)
   {
       for(int i=0;i<maxn;i++)
       {
           c1[i]=1;
           c2[i]=0;
       }
       for(int i=2;i<=17;i++)
       {
           for(int j=0;j<maxn;j++)
           {
               for(int k=0;k+j<maxn;k+=(i*i))
               {
                   c2[k+j]+=c1[j];
               }
           }
           for(int j=0;j<maxn;j++)
           {
               c1[j]=c2[j];
               c2[j]=0;
           }
       }
       cout<<c1[m]<<endl;
   }
   return 0;
}

2.   Ignatius and the Princess III

Description
"Well, it seems the first problem is too easy. I will let you know how foolish you are later." feng5166 says. 
"The second problem is, given an positive integer N, we define an equation like this: 
  N=a[1]+a[2]+a[3]+...+a[m]; 
  a[i]>0,1<=m<=N; 
My question is how many different equations you can find for a given N. 
For example, assume N is 4, we can find: 
  4 = 4; 
  4 = 3 + 1; 
  4 = 2 + 2; 
  4 = 2 + 1 + 1; 
  4 = 1 + 1 + 1 + 1; 
so the result is 5 when N is 4. Note that "4 = 3 + 1" and "4 = 1 + 3" is the same in this problem. Now, you do it!" 
Input
The input contains several test cases. Each test case contains a positive integer N(1<=N<=120) which is mentioned above. The input is terminated by the end of file. 
Output
For each test case, you have to output a line contains an integer P which indicate the different equations you have found. 
Sample Input
4
10
20
Sample Output
5
42
627

问4的组成方式的个数,这是典型的母函数,直接上代码了:

#include <iostream>

using namespace std;
#define maxn 130
int c1[maxn],c2[maxn];

int main()
{
   int m;
   while(cin>>m&&m)
   {
       for(int i=0;i<maxn;i++)
       {
           c1[i]=1;
           c2[i]=0;
       }
       for(int i=2;i<=maxn;i++)
       {
           for(int j=0;j<maxn;j++)
           {
               for(int k=0;k+j<maxn;k+=i)
               {
                   c2[k+j]+=c1[j];
               }
           }
           for(int j=0;j<maxn;j++)
           {
               c1[j]=c2[j];
               c2[j]=0;
           }
       }
       cout<<c1[m]<<endl;
   }
   return 0;
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值