【hdoj2079】选课时间(母函数 C语言 附带母函数的讲解)

题目链接
参考(用的通用模板)

首先讲一讲母函数。
假设有1g,2g,3g,4g的砝码各一个,问能称出几种重量?
在枚举计算重量时,我们是采用加法,即将不同克数相加可以得到不同的重量,如果放到数学式子里面,怎么模拟相加呢?答案是指数乘法,即 a^m * a^n = a^(m+n)
因此现在我们使用指数来模拟每种砝码取几个的克数,现在四种砝码各只有一个,因此只有0个和1个两种取法,那么克数分别为{0g,1*克数},因此可以得到如下算式:

每个因式(即每个括号)代表的即为每种砝码的取法,而经过化简后所得到的式子,每一项的指数代表的即为重量,而系数代表的即为这一重量的取法,例如a^7的系数为2,有{1,2,4}和{3,4}两种取法。

那么当每一种砝码有若干个时,式子应该怎么列出呢?
例如现在有2个1g砝码,1个2g砝码,模拟的即为hdoj2079本题的第一个样例。
2个1g砝码有0个,1个,2个三种取法,克数分别为0g,1g,2g,而2g砝码只有一个,仍只有0个和1个两种取法,克数分别为0g,2g,因此可以得到如下算式:

经过这两个例子应该对于母函数有比较基础的理解了,再举最后一个例子加深理解,现在有i个1g砝码,j个2g砝码,k个3克砝码……m个n克砝码
同理,可以得到如下式子:

这里就不化简了,这个式子化简后得到的每一项的指数即为重量,系数即为这一重量的取法。

现在回到题目,题目如下:

题目中的学分即为上例中砝码的克数,课程门数即为砝码个数,先把已AC代码放出:

#include<stdio.h>
#include<string.h>
int main(){
	int T,n,k,a[45],b[45];
	int c1[1000],c2[1000];
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&k);
		for(int i=1;i<=k;i++){
			scanf("%d%d",&a[i],&b[i]);
		}
		memset(c1,0,sizeof(c1));
		c1[0]=1;    //这里可以理解为初始是用a^0即1来乘之后的因式,系数为1,所以c1[0]=1
		for(int i=1;i<=k;i++){  //i指的是学分,也指式中的第i个因式
			memset(c2,0,sizeof(c2));
			for(int j=0;j<=b[i]&&j*a[i]<=n;j++){  //j指的是选择学分为i的课程的数量,即因式中的每一个项的指数
				for(int k=0;k+j*a[i]<=n;k++){  //将c1中存放的每一个项与第i个因式的第j个项相乘,即系数相加,然后放到过渡数组c2中
					c2[k+j*a[i]]+=c1[k];
				}
			}
			memcpy(c1,c2,sizeof(c2)); //将c2拷贝给c1,c1就是目前的前i个因式相乘得到的式子了
		}
		printf("%d\n",c1[n]);  //c1[n]即为n个学分的选课组合数
	}
	return 0;
} 

将学分和门数分别放入数组a[]和b[]中,c1[i]数组存放指数为i的项的系数,即可以理解为存放的是化简后的每一个项,即:

posted @ 2021-03-13 14:40  oCodeHoney  阅读(84)  评论(0)    收藏  举报