题目原链接:
http://acm.hdu.edu.cn/showproblem.php?pid=2082
知识点:
母函数(生成函数):
生成函数有普通型生成函数和指数型生成函数两种(本题是普通型)。
形式上,普通型母函数用于解决多重集的组合问题,
指数型母函数用于解决多重集的排列问题。
母函数还可以解决递归数列的通项问题(例如使用母函数解决斐波那契数列,Catalan数的通项公式)。
普通母函数:
构造母函数G(x), G(x) = a0 + a1*x + a2* + a3* +....+ an*, 则称G(x)是数列a0,a1…an的母函数。
通常普通母函数用来解多重集的组合问题,其思想就是构造一个函数来解决问题,一般过程如下:
1.建立模型:物品n种,每种数量分别为k1,k2,..kn个,每种物品又有一个属性值p1,p2,…pn,(如本题的字母价值),
求属性值和为m的物品组合方法数。(若数量ki无穷 也成立,即对应下面式子中第ki项的指数一直到无穷)
2.构造母函数:G(x)=(1++…)(1+++…)…(1+++…) (一)
=a0 + a1*x + a2* + a3* +....+ akk* (设kk=k1·p1+k2·p2+…kn·pn) (二)
G(x)含义: ak 为属性值和为k的组合方法数。
母函数利用的思想:
1.把组合问题的加法法则和幂级数的乘幂对应起来。
2.把离散数列和幂级数对应起来,把离散数列间的相互结合关系对应成为幂级数间的运算关系,最后由幂级数形式来
确定离散数列的构造。
代码实现:
求G(x)时一项一项累乘。先令G=1=(1+0*x+0*+…0*),再令G=G*(1++…)得到形式(二)的式子…最后令G=G*(1+++…)。
题目分析:
1.建模:物品(字母)26种,每种数量x1,x2…x26,属性值为1,2,3..26,求属性值和<=50的组合方法数。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int a[60],b[60];
// a[i]表示组合为i的组合方法数,b是中间值
int main()
{
//freopen("B.txt","r",stdin);
int n;
cin >> n;
while(n--)
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
a[0] = 1;
int num;
for(int i = 1;i <= 26;i++)
{
scanf("%d",&num);
for(int j = 0;j <= 50;j++) // 对应价值的组合 a[]
{
// k是数量 ,所以小于等于num,且价值不能超过50
for(int k = 0;k<=num && (k*i+j)<=50;k++)
b[j+k*i] += a[j];
}
// 把b的值赋给a,然后重新b置零
for(int j = 0;j <= 50;j++)
{
a[j] = b[j];
b[j] = 0;
}
}
// 最后得到了价值1-50的 各个组合数 ,最后全部加起来,就是答案
long long ans = 0;
for(int i = 1;i <= 50;i++)
ans += a[i];
cout << ans <<endl;
}
return 0;
}