题源:HDU2079
求组合数类型题目。母函数求解。
看了挺多博客都没看懂(可能输入的公式太难看),最后还是看的《算法竞赛入门到进阶》一书看懂了。也写一下作为如同我一样的蒟蒻怎么会用这玩意儿吧。
母函数
定义就不说了。公式为G(x)=(1+x+
x
2
x^2
x2+
⋯
\cdots
⋯)(1+
x
2
x^2
x2+
⋯
\cdots
⋯)(1+
x
3
x^3
x3+
⋯
\cdots
⋯)
如此题中给出的范例,假设需要计算达到40学分的组合数。学分和课数分别为(a,b):(1,1),(2,2),(3,2),(4,2),(5,8),(6,9),(7,6),(8,8)。
那么我们就可以知道1学分的课最少选0节,最多选1节
⋯
\cdots
⋯依此类推。每一种学分的课都是最少选0节,最多选b节。就可以得到这样的一个计算式。
G(x)=(
x
0
∗
1
+
x
1
∗
1
x^{0*1}+x^{1*1}
x0∗1+x1∗1)(
x
0
∗
2
+
x
1
∗
2
+
x
2
∗
2
x^{0*2}+x^{1*2}+x^{2*2}
x0∗2+x1∗2+x2∗2)(
x
0
∗
3
+
x
1
∗
3
+
x
2
∗
3
x^{0*3}+x^{1*3}+x^{2*3}
x0∗3+x1∗3+x2∗3)(
x
0
∗
4
+
x
1
∗
4
+
x
2
∗
4
x^{0*4}+x^{1*4}+x^{2*4}
x0∗4+x1∗4+x2∗4)(
x
0
∗
5
+
x
2
∗
5
+
⋯
+
x
8
∗
5
x^{0*5}+x^{2*5}+\cdots+x^{8*5}
x0∗5+x2∗5+⋯+x8∗5)(
⋯
\cdots
⋯);
很明显,在第一个括弧里是1学分课的所有情况:选一次,选两次···以后的每一个括弧同理。
x的次数就是选课的节数*此课的学分。最后的要求的n学分的组合数就是
x
n
x^n
xn的系数。
注意:
母函数运用时若不判断下标是否满足条件会出现下标越界的情况。
若较小的数据,可以直接定义一个很大的数组来防止下标越界。
若较大就必须判断下标是否情况,防止程序崩溃/出错。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
//学n学分 k a b k行,a学分的课有b门
for(int i=0;i<t;i++){
int n;
int k;
cin>>n>>k;
int score[9]={0};//最多8分,要从1开始,所以长度9
int result[50]={0};//保存结果
int temp[50]={0};//临时存储计算后的
int a,b;
for(int j=0;j<k;j++){
cin>>a>>b;
score[a]=b;//记录下学分为a的课有b门
}
for(int j =0;j<=score[1]&&j<=n;j++){//对初始学分为1的情况进行赋值
result[j]=1;
}
//40 8
// 1 1
// 2 2
// 3 2
// 4 2
// 5 8
// 6 9
// 7 6
// 8 8
//母函数
for(int now =2;now<=k;now++){//学分为一已经计算过了。
for(int j = 0;j<=n;j++){//遍历结果
for(int l=0,m=0;l<=score[now]&&m+j<=n;l++,m+=now){
temp[m+j]+=result[j];
}
}
for(int j=0;j<=n;j++){
result[j]=temp[j];
temp[j]=0;
}
}
cout<<result[n]<<endl;
}
return 0;
}
相同类型的题:HDU 1028。