母函数

                      

有1克、2克、3克、4克的砝码各一枚,能称出哪几种重量?每种重量各有几种可能方案? 

考虑用母函数来接吻这个问题:

我们假设x表示砝码,x的指数表示砝码的重量,这样:

1个1克的砝码可以用函数1+x表示,

1个2克的砝码可以用函数1+x2表示,

1个3克的砝码可以用函数1+x3表示,

1个4克的砝码可以用函数1+x4表示,

几种砝码的组合可以称重的情况,可以用以上几个函数的乘积表示:

(1+x)(1+x2)(1+x3)(1+x4)

=(1+x+x2+x3)(1+x3+x4+x7)

=1+x+x2+2x3+2x4+2x5+2x6+2x7+x8+x9+x10 

从上面的函数知道:可称出从1克到10克,系数便是方案数。(!!!经典!!!)

    例如右端有2x5 项,即称出5克的方案有2:5=3+2=4+1;同样,6=1+2+3=4+2;10=1+2+3+4。

 

 

 

 

 

 

 

  1. 整数拆分

输入一个整数n,把它拆成若干个整数相加,输出有多少种拆分的方法。

比如:4=3+1=2+2=2+1+1=1+1+1+1,有5种。

 

解:

所谓整数拆分即把整数分解成若干整数的和(相当于把n个无区别的球放到n个无标志的盒子,盒子允许空,也允许放多于一个球)。

 

G(x)=(1+x+x^2+…)(1+x^2+x^4+…)(1+x^3+x^6+…)…

 

#include <stdio.h>

#include <string.h>

int main()

{

    int c1[200],c2[200];     //c1保存当前表达式里各项的系数,c2是中间量

    int n,i,j,k;

    while(scanf("%d",&n)!=EOF)  

    {

        for(i=0;i<=n;i++)   //n拆分的数必定小于或等于n,所以只用从0遍历到n,即母函数的第1个括号里的表达式只能是1+x+x^2+…+x^n

        {

            c1[i]=1;          //初始化c1[],c1[]的下标是项的指数,值是项的系数

            c2[i]=0;

        }

        for(i=2;i<=n;i++)       //后面有n-1个表达式(括号里的式子),要展开n-1次,i指第i个表达式

        {

            for(j=0;j<=n;j++)

            {

                for (k=0;k+j<=n;k=k+i)

                {

                    c2[k+j]+=c1[j];        //只用保留指数<=n的项,所以k+j<=n

                }                        //j指当前表达式里项的第j个项,k指后一个表达式里项的指数,k=k+i

            }                            //两个相邻表达式展开

            for(j=0;j<=n;j++)

            {

                c1[j]=c2[j];

                c2[j]=0;

            }            //每展开一次,就将c1[]更新,c2[]归0

        }

        printf("%d\n",c1[n]);

    }

    return 0;

}

 

  1. 钱币组合:

有面值为1,2,5的3种硬币,输入各硬币的个数,求最小的一个不能组合的成的价值。

比如输入1 1 3,输出4

 

解:

G(x)=(1+x+x^2+…)(1+x^2+x^4+…)(1+x^5+x^10+…)

3种面值,所以只有3个括号表达式。

 

#include <stdio.h>
#include <string.h>
int main()
{
    int a,b,c,i,j,k,sum;
    int c1[8008],c2[8008];
    int elem[3]={1,2,5};
    int num[4];
    while(scanf("%d%d%d",&a,&b,&c)!=EOF)
    {
        if(a==0&&b==0&&c==0)
            break;
        sum=1*a+2*b+5*c;
        num[1]=a;
        num[2]=b;
        num[3]=c;     //num[]存放括号内表达式的项的个数
 
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2));     //c1[],c2[]全部归0
        for(i=0;i<=a;i=i+elem[0])    //初始化第1个表达式里各项的系数
            c1[i]=1;
    
        for(i=2;i<=3;i++)
        {
            for(j=0;j<=sum;j++)
            {
                for(k=0;k<=num[i]*elem[i-1]&&k+j<=sum;k=k+elem[i-1])
                {
                    c2[j+k]+=c1[j];
                }
            }
            for(j=0;j<=sum+1;j++)
            {
                c1[j]=c2[j];
                c2[j]=0;
            }
        }
        
        for(i=0;i<=sum+1;i++)
        {
            if(c1[i]==0)
            {
                printf("%d\n",i);
                break;
            }
        }
    }
    return 0;
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值