算法归纳:母函数(待更新)

文章来自:http://blog.sina.com.cn/s/blog_8f06da990100uz2j.html

先说明一下母函数,对于序列a0a1a2...构造一函数G(x)=a0+a1x+a2x^2+...,则称G(x)a0a1a2...的母函数。

母函数可以解决很多计数问题,比如说有三种颜色的球各一个,问有多少种组合方式呢?41的整数划分有多少种?诸如此类的好多,这次只是讲一下解决acm中的常遇到的一种问题,我们用砝码作为模型来描述。

现在给你几种不同重量的砝码,每种砝码的数量都不同,那么用这些砝码组合成某个重量的方法数为多少呢?

 

一个1克砝码可以表示10克重量(0),11克重量(1);

两个1克砝码可以表示10克重量(0),11克重量(1),12克重量(1+1

三个1克砝码可以表示10克重量(0),11克重量(1),12克重量(1+1),13克重量(1+1+1

 

一个2克砝码可以表示10克重量(0),12克重量(2);

两个2克砝码可以表示10克重量(0),12克重量(2),14克重量(2+2

三个2克砝码可以表示10克重量(0),12克重量(2),14克重量(2+2),16克重量(2+2+2

 

一个m克砝码可以表示10克重量(0),1m克重量(m

nm克砝码可以表示1种0*m克重量(0),1种1*m克重量(m),1种2*m克重量(m+m),…,1种n*m克重量(m+m…+m)

 

我们用函数来描述上述的情况

一个1克砝码1*x^0+1*x^1

两个1克砝码:1*x^0+1*x^1+1*x^2

三个1克砝码:1*x^0+1*x^1+1*x^2+1*x^3

 

一个2克砝码1*x^0+1*x^2

两个2克砝码:1*x^0+1*x^2+1*x^4

三个2克砝码:1*x^0+1*x^2+1*x^4+1*x^6

 

一个m克砝码1*x^0+1*x^m

nm克砝码:1*x^(0*m)+1*x^(1*m)+1*x^(2*m)+1*x^(3*m)+…+1*x^(n*m)

 

 

我们把函数连乘,比如1克砝码2个,2克砝码3个,3克砝码四个,我们用函数表示为

两个1克砝码:1*x^0+1*x^1+1*x^2

三个2克砝码:1*x^0+1*x^2+1*x^4+1*x^6

四个3克砝码:1*x^0+1*x^3+1*x^6+1*x^9+1*x^12

用这些砝码可以组合多少种重量呢,就是把他们能表示的各种重量进行叠加组合,方法是这三条式子相乘。

(1*x^0+1*x^1+1*x^2)(1*x^0+1*x^2+1*x^4+1*x^6)(1*x^0+1*x^3+1*x^6+1*x^9+1*x^12)

得到a1*x^b1+a2*x^b2+a3*x^b3+an*x^bn

这个式子可以还原为:有a1种方式组合成重量b1,有a2种方式组合成重量b2,有an种方式组合成重量bn

像求任意数量的任意砝码能组合成多少种重量或者某种重量有多少种组合方法都可以解决。

 

接下来是代码实现,算是模板吧

//target目标重量,n代表砝码的种数,(注意:因子不一定从1开始)   

//a[i]表示重量为i的方式数,x^i的系数,设置temp[]数组来保存中间结果   

//fweight[i]记录第i种砝码的重量

//fnumber[i]记录第i种砝码的数量

memset(temp,0,sizeof(temp));

memset(a,0,sizeof(a)); 

for(int i=1;i<=n;i++)

scanf("%d%d",&fweight[i],&fnumber[i]); 

for(int i=0;i<=fweight[1]*fnumber[1];i+=fweight[1])

a[i]=1;//先对第一个砝码初始化   

for(int i=2;i<=n;i++)//依次将编号2到n的砝码相乘,每次相乘结果保存在a[]中 

{ 

     for(int j=0;j<=target;j++)//temp[j]表示被乘数的x^j的系数。也即指前i个表达式乘完之后的x^j的系数   

     { 

         for(int k=0;k+j<=target&&k<=fweight[i]*fnumber[i];k+=fweight[i])//k表示当前第i个式子的指数   

         { 

              temp[k+j]+=a[j];//k+j表示被乘数x^j的系数与乘数x^k的系数之和。即x^j*x^k的系数   

         } 

     } 

 

     for(int j=0;j<=target;j++)//每乘完一个式子  

     { 

         a[j]=temp[j]; //将结果保存在a[]中

 temp[j]=0;//将中间组数清零继续进行下一轮乘法运算   

     } 

} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值