有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。
- 整数拆分
输入一个整数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,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;
}