下面这个例子来自HDU集训的PPT
“例1:若有1克、2克、3克、4克的砝码各一 枚,能称出哪几种重量?各有几种可能方案?理解了这个例子之后整数拆分、邮票组合、砝码称重一类的问题就都一并解决了。
如何解决这个问题呢?考虑构造母函数。如果用x的指数表示称出的重量,则:
1个1克的砝码可以用函数1+x表示,1个2克的砝码可以用函数1+x2表示,
1个3克的砝码可以用函数1+x3表示,1个4克的砝码可以用函数1+x4表示,
几种砝码的组合可以称重的情况,可以用以上几个函数的乘积表示:
(1+x)(1+x^2)(1+x^3)(1+x^4)
=(1+x+x^2+x^3)(1+x3+x^4+x^7)
=1+x+x^2+2x^3+2x^4+2x^5+2x^6+2x^7+x^8+x^9+x^10
从上面的函数知道:可称出从1克到10克,系数便是方案数。
”
两个母函数练手题,性质都是一样的
HDU1085(母函数) Holding Bin-Laden Captive!
http://acm.hdu.edu.cn/showproblem.php?pid=1085
硬币面额1,2,5且有数量限制num1,num2,num3,问最小不能组合的数量是多少。
G(x)=(1+x+...+x^num1)(1+x^2+...+x^2num2)(1+x^5+,,,+x^5num3),展开,系数不为0的数都是可以由硬币组合出来的。
#include
#include
using namespace std;
const int maxexp=1*1000+2*1000+5*1000+10;
int main()
{
int n1, n2, n3;
bool f;
int c1[maxexp+1], c2[maxexp+1], c3[maxexp+1];
while (scanf("%d%d%d", &n1, &n2, &n3)==3 && n1+n2+n3>0)
{
memset(c1, 0, sizeof(c1));
memset(c2, 0, sizeof(c2));
memset(c3, 0, sizeof(c3));
for (int i=0; i<=n1; i++) //1+x+x^2+...+x^n1
c1[i]=1;
for (int j=0; j<=n1; j++)
for (int k=0; k<=2*n2; k+=2) //1+x^2+...+x^(2*n2)
c2[j+k]+=c1[j];
for (int j=0; j<=n1+2*n2; j++)
for (int k=0; k<=5*n3; k+=5) //1+x^2+...+x^(5*n2)
c3[j+k]+=c2[j];
f=false;
for (int j=0; j<=n1+2*n2+5*n3; j++)
if (c3[j]==0)
{
printf("%d\n", j);
f=true;
break;
}
if (!f) printf("%d\n", n1+2*n2+5*n3+1);
}
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=1028
整数拆分。由于拆分结果只考虑有几个1几个2几个3...不考虑顺序什么的,那么问题就和之前的砝码邮票什么的一样了。
G(x)=(1+x+...)(1+x^2+...)(1+x^3....)...(1+x^n)
n最大120,可以预处理一下保存结果,也可以一边输入一边重新算,反正n很小……
#include
#include
using namespace std;
const int maxexp=120;
int main()
{
int n, now, pre;
int f[2][maxexp+1]; //"滚筒"使用
while (scanf("%d", &n)==1)
{
memset(f, 0, sizeof(f));
for (int i=0; i<=n; i++) //1+x+x^2+...+x^n
f[1%2][i]=1;
for (int i=2; i<=n; i++)
{
now=i%2; pre=(i-1)%2;
for (int j=0; j<=n; j++)
f[now][j]=0;
for (int j=0; j<=n; j++) //枚举之前结果
for (int k=0; k+j<=n; k+=i) //枚举当前多项式 1+x^i+x^2i+...
{
f[now][j+k]+=f[pre][j]; //中间结果保存在c2
}
}
printf("%d\n", f[n%2][n]);
}
return 0;
}
HDU2013
赤裸裸的递推,毫无技术性而言
f(n)=(f(n-1)+1)*2, 从最后一天逆推。
#include
#include
int main()
{
int f[32], n;
f[1]=1;
for (int i=2; i<30; i++)
f[i]=(f[i-1]+1)*2;
while (scanf("%d", &n)==1)
printf("%d\n", f[n]);
return 0;
}