生成函数入门

目录

定义

作用

模板

题目

hdu1085

hdu1171

hdu1398

hdu2152

hdu1709

hdu2069

hdu2065

hdu1521


生成函数即母函数,是组合数学中尤其是计数方面一个重要理论和工具。

定义

对于任意数列a_{0},a_{1},a_{2},a_{3}\cdots用一个函数G\left(x \right )=a_{0}+a_{1}x^{1}+a_{2}x^{2}\cdots联系起来,我们称G(x)为该数列的生成函数

-----------------------------------------------------------

作用

我们可以用生成函数来求出斐波那契数列的通项公式,解法如下:

G\left(x \right )=0+1*x^{1}+1*x^{2}+2*x^{3}+3*x^{4}+5*x^{5}+\cdots

xG\left(x \right )=0*x^{1}+1*x^{2}+1*x^{3}+2*x^{4}+3*x^{5}+5*x^{6}+\cdots

G\left(x \right )+xG\left(x \right )=0+1*x^{1}+2*x^{2}+3*x^{3}+5*x^{4}+8*x^{5}+\cdots

G\left(x \right )+xG\left(x \right ) 相当于G(x)的序列全部左移一下再去掉多余的1即G\left(x \right )+xG\left(x \right )=\frac{G\left(x \right )}{x}-1

立即推G\left(x \right )=\frac{x}{1-x-x^{2}} ,其实还可以继续化简,我就不化了

---------------------------------------------------------------

从一道简单的题目开始练起

我们有1克,2克,3克,4克的砝码各一个,还有一个天平,假设天平左边不能放砝码的话(左物右码),能称出哪些重量的物品,并求出对应方案数

解:我们用a_{n}x^{n}来表示方案,其中a_{n}表示方案数,x^{n} 表示重量

则只用1克可以称出1+x^{1} ,1表示不放

只用2克可以称出1+x^{2} ,同理3的1+x^{3} ,4的1+x^{4} ,最后因为相互独立,可以乘起来

G\left(x \right )\\=\left(1+x^{1} \right )\left(1+x^{2} \right )\left(1+x^{3} \right )\left(1+x^{4} \right )\\=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}

例如重量为6的有1,2,3和2,4两种方案

--------------------------------------------------------------

这次我们有质量为a1,a2,a3,a4...ak克的砝码各一个,还有一个天平,左右两边都能放但是物品只能放左边,问重量为n克的物品有几种称法

解我们用x^{-a_{i}}+1+x^{a_{i}} 来表示第i个砝码,其中x^{-a_{i}} 表示质量为ai的砝码放在天平左边,1表示不放,x^{a_{i}}表示放在右边

因为相互独立乘起来就可以的到G(x),最后输出x^{n}的系数

----------------------------------------------------------

模板

下面给出模板(系数都是1的那种)

#include
#include//memset和memcpy都在这里
int main(){
	const int manN=100001;
	int n;
	//a是x的系数,b是中间变量,v是每个物品的价值,n1和n2是物品的最少数量和最多数量 
	int a[maxN],b[maxN],v[n],n1[n],n2[n];
	memset(a,0,sizeof(n);
	a[0]=1;
	for(int i=0;i


如果数据量比较大,可以考虑用第二个模板

 

#include
#include//memset和memcpy都在这里
int main(){
	const int manN=100001;
	int n;
	//a是x的系数,b是中间变量,v是每个物品的价值,n1和n2是物品的最少数量和最多数量 
	int a[maxN]={1},b[maxN],v[n],n1[n],n2[n],last=0,last2;//last是上一次乘完之后最大的幂,last2是这次最大能到达的幂 
	for(int i=0;i

---------------------------

题目

来点题目试一下

hdu1085

题目大意是说,给你1,2,5这几个硬币,每一个有a,b,c个,问你最小的不能达到的价值是多少

模板题

 

#include
#include
int main(){
    const int maxN=9000;
    //n数组是每个硬币有多少个,v是硬币的价值 
    int a[maxN],b[maxN],n[3],v[3]={1,2,5},last,last2;
    while(scanf("%d%d%d",&n[0],&n[1],&n[2])&&n[0]+n[1]+n[2]>0){
        last=0;
        a[0]=1;
        for(int i=0;i<3;++i){
            last2=last+v[i]*n[i];//硬币是没有最大数的限制的,所以不需要min(maxN 
            memset(b,0,sizeof(int)*(last2+1));
            for(int j=0;j<=last;++j)
                for(int k=0;k<=n[i]&&j+k*v[i]<=last2;++k)b[j+k*v[i]]+=a[j];
            memcpy(a,b,sizeof(int)*(last2+1));
            last=last2;
        }
        int result=0;
        for(result=0;result<=last2;++result)//找到第一个达不到价值(有可能从0到last2都能达到,所以不能中间输出后break 
            if(!a[result])break;
        printf("%d\n",result);
    }
    return 0;
}


-----------------------------------------------------

 

hdu1171

题目大意有n个物品,每个物品都有价值v和数量m,要分成两堆,使价值尽量接近,

解:也是个模板题,得到所有系数后,从总价值的一半开始往前搜索,得到第一个系数不为0的

#include
#include
int main(){
    const int maxN=250010;
    int a[maxN],b[maxN],n2[50],v[50],last,last2,n;
    while(scanf("%d",&n)&&n>=0){
        for(int i=0;i>1;
        while(!a[result])--result;
        printf("%d %d\n",last-result,result);
    }
    return 0;
}

-----------------------------------------------------------

 

hdu1398

题目大意:有几种硬币价值为1的平方到17的平方,每种硬币数量不限,问价值n的组成方法有几种

模板题

 

#include
#include
int main(){
    const int maxN=301;
    int a[maxN]={1},b[maxN],n;
    for(int i=1;i<=17;++i){
        memset(b,0,sizeof(b));
        for(int j=0;j

---------------------------------------

 

hdu2152

中文题,这次每个价值有上下限了

 

#include
#include
int main(){
    const int maxN=101;
    int a[maxN],b[maxN],n1[maxN],n2[maxN],last,last2,n,m;
    while(scanf("%d%d",&n,&m)!=EOF){
        for(int i=0;i=m&&a[m])printf("%d\n",a[m]);
        else printf("0\n");
    }
    return 0;
}

 

-----------------------------

hdu1709

题目大意:给你几个砝码,左右两边都能放,问哪些质量不能被称到

解:

 

#include
#include
#include
int main(){
    const int maxN=10001;
    int a[maxN],b[maxN],v[100],result[100],last,last2,n,cnt;
    while(scanf("%d",&n)!=EOF){
        for(int i=0;i

 

-----------------------------------------

hdu2069

题目大意:有1,5,10,25,50这几种硬币,要组成价值n有几种方案,用的硬币数量加起来不能超过100个

解:这是一道二维的母函数,数据量不大,控制一下硬币的数量和价值,硬肛就行了

 

#include
#include
int main(){
    int a[251][101]={1},b[251][101]={},v[]={1,5,10,25,50},result[251]={1},n;
    for(int i=0;i<5;++i){
        for(int j=0;j<=250;++j)
            for(int k=0;j+k*v[i]<=250;++k)
                for(int p=0;k+p<=100;++p)b[j+k*v[i]][k+p]+=a[j][p];
        for(int j=0;j<=250;++j)
            for(int k=0;k<=100;++k){
                a[j][k]=b[j][k];
                b[j][k]=0;
            }
    }
    for(int i=1;i<=250;++i)
        for(int j=0;j<=100;++j)result[i]+=a[i][j];
    while(scanf("%d",&n)!=EOF)printf("%d\n",result[n]);
    return 0;
}

---------------------------------

hdu2065

解:指数型生成函数,比较难一点,取n个为x^{n} 次方,但是取哪个都无所谓,\frac{x^{n}}{n!},最后要用泰勒公式化简

指数型生成函数\left(x+\frac{x^{2}}{2!}+\frac{x^{4}}{4!}+\cdots \right )^{2}\left(1+x+\frac{x^{2}}{2!}+\frac{x^{3}}{3!}+\cdots \right )^{2}
由泰勒公式  \left\{\begin{matrix} e^{x}=1+x+\frac{x^{2}}{2!}+\frac{x^{3}}{3!}+\cdots\\ e^{-x}=1-x+\frac{x^{2}}{2!}-\frac{x^{3}}{3!}+\cdots \end{matrix}\right.

\left (\frac{e^{x}+e^{-x}}{2} \right )^{2}e^{2x}\\ =\left(\frac{e^{2x}+1}{2} \right )^{2}\\ =\frac{e^{4x}+2e^{2}+1}{4}\\ =\frac{\left(\left (1+4x+\frac{\left(4x \right )^{2}}{2!}+\cdots \right )+2\left(1+2x+\frac{\left(2x \right )^{2}}{2!}+\cdots \right )+1\right )}{4}\\ =\sum_{i=0}^{n-1}\frac{4^{i}+2*2^{i}}{i!*4}x^{i}+\frac{1}{4}\\ =\sum_{i=0}^{n-1}\frac{4^{i-1}+2^{i-1}}{i!}x^{i}+\frac{1}{4}
所以x的n次方的系数4^{n-1}+2^{n-1}

#include
const int mod=100;
//快速幂 
int quickMod(int a,long long int b){
    int result=1;
    while(b){
        if(b&1)result=result*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return result;
}
int main(){
    int t;
    long long int n;//指数型生成函数(x+x^2/(2!)+x^4/(4!)+....)^2(1+x+x^2/(2!)+x^3/(3!)+....)^2
    //由泰勒公式e^x=1+x+x^2/(2!)+x^3/(3!)+.... e^(-x)=1-x+x^2/(2!)-x^3/(3!)+....
    //原式=((e^x+e^(-x))/2)^2 *e^2x=((e^2x+1)/2)^2=(e^4x+2e^2x+1)/4=(1+4x+(4x)^2/2!+....+2(1+2x+(2x)^2/2!)+....+1)/4
    //=(sigma(i=0到n-1)(4^i+2*2^i)/i!)x^i/4+1/4=sigma(i=0到n-1)(4^(i-1)+2^(i-1))x^i+1/4
    //所以x的n次方的系数 4^(n-1)+2^(n-1)
    while(scanf("%d",&t)&&t){
        for(int i=1;i<=t;++i){
            scanf("%lld",&n);
            printf("Case %d: %d\n",i,(quickMod(4,n-1)+quickMod(2,n-1))%mod);
        }
        printf("\n");
    }
    return 0;
}

-------------------------------------------------

 

hdu1521

指数型母函数

 

#include 
#include
int main(){
    const int maxN=11;
    long long int fact[maxN]={1};
    for(int i=1;i

 

 

 

 

hdu1028,2079,2082,2110都可以去练一下
 

 

 

 

你可能感兴趣的:(数据结构与算法)