People in Silverland use square coins. Not only they have square shapes but also their values are square numbers. Coins with values of all square numbers up to 289 (=17^2), i.e., 1-credit coins, 4-credit coins, 9-credit coins, ..., and 289-credit coins, are available in Silverland.
There are four combinations of coins to pay ten credits:
ten 1-credit coins,
one 4-credit coin and six 1-credit coins,
two 4-credit coins and two 1-credit coins, and
one 9-credit coin and one 1-credit coin.
Your mission is to count the number of ways to pay a given amount using coins of Silverland.
Input
The input consists of lines each containing an integer meaning an amount to be paid, followed by a line containing a zero. You may assume that all the amounts are positive and less than 300.
Output
For each of the given amount, one line containing a single integer representing the number of combinations of coins should be output. No other characters should appear in the output.
Sample Input
2
10
30
0
Sample Output
1
4
27
题意:给你无限个面值为1,4,9,16.....289的硬币
给你一个n ,问你用这些的硬币能用多少种方案凑出n
使用母函数求解
母函数,又称生成函数,是用于解决组合问题计数的一种方法。
引例:
在了解它之前我们先看看熟悉的杨辉三角。
杨辉三角的第n行(注意是从0开始标号的)的数字就是(1+x)n
的展开式从低项到高项的各项系数,也可以表示为组合数的形式Cin。如果将两者联系起来我们会发现,(1+x)可以看成对于一件取舍,1=x0就是不取,x就是取。这样在(1+x)n的展开式中xi项的系数就是从n件物品选取i件的方案数。
定义
给定数列a0,a1,a2…an
,构造函数G(x)=a0f0(x)+a1f1(x)+a2f2(x)…anfn(x),其中G(x)就是该序列的母函数,f0(x),f1(x),f2(x)…fn(x)
为标志函数。
母函数主要有两种形式:普通型母函数和指数型母函数。
普通型母函数
先看一个例题:HDU 1085
普通型母函数的标志函数一般为x0,x1,x2…xn
因为每个硬币有个数限制,但是也不难构造出
G(x)=(1+x+x2+x3+…+xnum1)(1+x2+x4+…+x2∗num2)(1+x5+x10+…+x5∗num5)
。将多项式展开后,xi项对应的系数就是组成面值为i的方案数。
指数型母函数
再看一个例题:HDU 1521
指数型母函数的标志函数一般为x0/0!,x1/1!,x2/2!...xn/n!
,对于xii!表示在一个方案中某个元素出现了i次,而不同位置的该种元素本质不同,所以在记方案数时只算作一种,所以最后结果应处以i!
。
对于这道题就不难构造出母函数为
G(x)=(10!+X/1!+X2/2!+...+Xa1/a1!)(10!+X1!+X2/2!+...+Xa2/a2!)(10!+X1!+X2/2!+...+Xan/an!)
这里先给出两句话
1.“把组合问题的加法法则和幂级数的乘幂对应起来”
2.“母函数的思想很简单 — 就是把离散数列和幂级数一 一对应起来,把离散数列间的相互结合关系对应成为幂级数间的
运算关系,最后由幂级数形式来确定离散数列的构造. “
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 300 + 1;
int main()
{
int i,j,k;
int a[N],b[N];
for(i=0;i<N;i++)
{
a[i]=1; // 模拟第一个括号,各项系数为1(有可能不全为1)
b[i]=0; // 辅助数组
}
for(i=2;i*i<N;i++) // i 表示 第几个括号 i++ 使被乘的括号后移一个
{
for(j=0;j<N;j++) // j 表示引用第一个括号中的第j个
{
for(k=0;k+j<N;k+=i*i) // k 表示第二个括号中每一项的x的次数
{
b[k+j]+=a[j]; // k+j 表示第一个括号的第j个项 与 第二个括号中的每一项的 乘积的次数 b数组记录次数为k+j的系数 a数组为第一个括号第j个的系数 超过 N 的没统计
}
}
for(j=1;j<N;j++)
{
a[j]=b[j]; // 更新第一个括号的各项的系数
b[j]=0; // 先更新被乘的括号的各项的系数(应该是1,但为了之后进行加法运算,例如:x×x之后x的平方的系数为1) 循环后 i++ 使被乘的括号后移一个
}
}
int n;
while(scanf("%d",&n)!=EOF&&n)
printf("%d\n",a[n]);
return 0;
}
感谢一下三位大佬:
(定义)https://blog.csdn.net/chn_jz/article/details/73065465
(理解)https://blog.csdn.net/vsooda/article/details/7975485
(应用)https://blog.csdn.net/xiaofei_it/article/details/17042651