题目描述:有N块砖头,摆成阶梯状,要求阶梯高度必须严格递增。给定N,求所有不同的摆法总数。
原题目见:http://acm.jlu.edu.cn/joj/showproblem.php?pid=1026
方法一:
N块砖头至少摆出两阶,因此第一阶的砖头数目最大为floor((n-1)/2)。设f(i,j)表示i块砖头
摆成的阶梯中第一阶摆了j块砖头的所有摆法总数。那么,给定砖头总数N,所要求的答案就是
f(N,1)+f(N,2)+...+f(N,floor((n-1)/2))................(*)。
设i块砖头第一阶为j块砖头的某种摆法为j,n1,n2,...,nm。其中ni表示第i+1阶的砖头数。则有
j<n1<n2<...<nm,且有j+n1+n2+...+nm=i,其中1<=j<=floor((i-1)/2),m>=1,将其移项,得:
n1+n2+...+nm=i-j
而这个式子就表示i-j块砖头第一阶为n1块砖头的一种摆法。其中:
j+1<=n1<=floor((i-j-1)/2),因此i块砖头的问题可以转化为i-j块砖头的问题。于是:
f(i,j)=f(i-j,j+1)+f(i-j,j+2)+...+f(i-j,floor((i-j-1)/2)。
给定N块砖头,所有不同的摆法就是式(*),在(*)中:
f(N,1)=f(N-1,2)+f(N-1,3)+...+f(N-1,floor((N-1-1)/2);
f(N,2)=f(N-2,3)+f(N-2,4)+...+f(N-2,floor((N-2-1)/2);
......
在上述的分析中,是假定砖头至少要被分成两堆的,对于本来只分为两堆的情形,经状态转化后
就会把它作为0计算,比如:
f(8,3)=f(5,4)=0,但是8可以分为3,5这样的情形的。所以需要把漏掉的这一组解加上去。
代码为:
解法二:生成函数
题目的实质就是把N分成一些分部量不同的数的和的整数拆分问题。借助于生成函数,对于这个问题,
就是说对于一个数t,1<=t<=N,t或者不出现,或者只出现一次。所以,生成函数为:
G(x)=(1+x)(1+x^2)(1+x^3)...(1+x^n)
那么,G(x)的展开式中x^n的系数就是各个分部量不同的整数拆分问题。其中要减掉1*x^n的这种拆分
情况。也就是最终结果的x^n的系数减1就是本题要求的答案。
代码如下:
上述生成函数的代码可以写的更高效一些:
上面同样是生成函数的思想,但两段实现代码效率差别是非常巨大的,第一段代码提交成绩为0.05秒,第二段
代码为0.00秒。只是一个非常小的改动何以效率差别会如此巨大呢?
仔细分析,原因非常明确。第一段代码中,自己的for循环是这样写的: