今天我看我那本很久以前买的本来想当编程参考书的后来因为既看不懂又没有啥用途而扔下的《组合数学》来着,下午看到“特殊计数序列”这一章,又看到了久违的Catalan数,思考了一下,在这里菜菜地写下一点心得供天下大牛们鄙视。
Catalan数者,Cn=C(2n,n)/(n+1)也。举前几个例子:
C0=1
C1=1
C2=2
C3=5
C4=14
C5=42
C6=132
C7=429
C8=1430
C9=4862
这些数有什么递推的规律呢?
Cn= C0 * Cn-1 + C1 * Cn-2 + C2 * Cn-3 + ... + Cn-1 * C0
(这里面不好打sigma,大家凑和看吧。)
Cn(Catalan序列的第n个数)可以表示:
①有n个节点的不同形态的二叉树有多少种;
②把(严谨一点,凸的)n+2边形沿某几条对角线分割成n个三角形的方法数。
③排队买票问题。这是一个经典的问题,凡是高二及高二以上的都应该见过。2n个人排队买票,票价0.5元,有n个人拿的是1元的钱,n个人拿的是0.5元的钱,售票处没有零钱,问有多少种排队方法,使得售票处不会出现找不出零钱的局面?(每个拿着1元钱的人看作是相同的,每个拿着0.5元钱的人也看作是相同的)
④出栈序列。解释一下“栈”是什么意思。栈是计算机中一个线性的存储数据的结构,可以让某个数进栈,也可以从栈里取出数。栈里面可以存放许多数,每次取出的数,都是进栈最晚的那个数。现在有1,2,3..n这么多数来按顺序进栈了,但是什么时候从栈里取出数是没有要求的。这些数出栈的序列有几种呢?有Cn种。
⑤我好像就想出来了这4种,可能还有,我没想起来,谁知道就提醒我一下……
下面我来说一下这4种问题的本质都是一样的,都满足Cn= C0 * Cn-1 + C1 * Cn-2 + C2 * Cn-3 + ... + Cn-1 * C0 这个递推关系。
①:这个最好解释。拿出来一个结点作为树根,剩下n-1个结点,取k个作为左子树,n-k-1个作为右子树,左子树有Ck种形态,右子树有Cn-k-1种形态,乘起来(乘法原理嘛……),再把k的每个取值都算一下,最后sigma一下,就是那个递推的式子。
②:跟①有点像。先确定一条边e,从多边形的“对面”选一个顶点,与e的两端分别连接两条对角线,中间就形成了一个三角形。三角形的两侧,是两个凸多边形,假设一个是k+2边形,一个是n-k+1边形。k+2边形又有Ck种分法,n-k+1边形又有Cn-k-1种分法,用乘法原理乘起来就是总共的分法。把k的每一种情况都加起来,又一次得到了那个递推式子。
④:1肯定是第一个进栈的。观察1什么时候出栈。当1即将出栈时,已经有k个数出了栈,那么这k个数有Ck种出栈序列。1出栈后,没有进栈的n-k-1个数也有Cn-k-1种出栈序列。Ck * Cn-k-1就这样出现了。最后再sigma就行。
③:跟④表面上看就能看出来是等价的。把拿着1元钱的看成让下一个数进栈,把拿着0.5元钱的看成从栈里拿出来一个数,就完全变成④了。所以④的推理让我得出了③的推理。
把拿着1元钱的记为1,把拿着0.5元钱的记为0,组成一个0、1的序列。对于任意一个数m,序列里前m个数里面总是1的个数大于0的个数。这就是这个序列的一个规律(可以说是“终极规律”)。售票处的钱总是会有一个时刻变成0的(这是迟早的事!)当第一个这样的特殊时刻出现的时候,肯定对应0、1序列里面的一个0.把这个0拿掉,把序列的第一个1也拿掉,1跟0之间有k个1,k个0,这2k个单独拿出来组成的小序列满足“终极规律”(Ck!!!),那个特殊的0后面的n-k-1个0和n-k-1个0单独拿出来,当然都满足“终极规律”(Cn-k+1!!!)。这样也有了。一乘。一加。完事了。
①跟②是相似的问题,③跟④是相似的问题,下面把①转化成④。我画一棵二叉树:
○
/ \
○ ○
/
○
\
○
这个根和第一个进栈的元素其实是一样的。给二叉树做一下前序遍历(前序遍历是啥我用解释不?解释一下吧。就是,先处理根节点,再处理左子树,再处理右子树。对于每个子树的处理与处理整棵树相似(它的根节点,再左子树,再右子树)),遍历这个结点的时候,这个节点进栈,等它的左子树遍历完,让它出栈,再遍历它的右子树(是不是不学计算机的感觉像天书?_? =_=)。这样,每一棵树就跟每一个出栈序列一一对应起来了。问题①转化成了问题④。上图对应的是:1(进)2(进)3(进)3(出)4(进)4(出)2(出)1(出)5(进)5(出)。
上面是我解释了这个递推式子。还有那个通项公式Cn=C(2n,n)/(n+1)。这个证明是我从书上看的。我自己是想不出来的。我只不过是用自己的话表述了一下:
考虑那个n个0、n个1组成的串。如果n个0、n个1随便排,那就是C(2n,n)种排列方法(没问题吧?)现在看看其中不合法的序列有多少种。假设从第一个数到第2m个数都是合法的,第2m+1个数就不合法了,也就是,前2m个数有m个0,m个1,是合法的,第2m+1个数是0(这个人第一次使售票员找不开钱!)。把这个0后面的一直到末尾所有数1变成0,0变成1,整个序列就成了n-1个1,n+1个0。这样的排列是任意的,也就是,任意n-1个1,n+1个0组成的排列都对应着一个不合法的0、1序列。一共有C(2n,n-1)种。结果就是C(2n,n)-C(2n,n-1)=C(2n,n)/(n+1)。
-----------------------
后记:我本以为自己已经明白了,可以写出来教别人了,但是一到写的时候忽然发现,我还有很多细节忽略掉了。现在我可是明白了!
<转>http://dfs35123.spaces.live.com/blog/cns!A6CB032EC0980F16!523.entry?wa=wsignin1.0&sa=801812594
令h(0)=1,h(1)=1,catalan数满足递归式: h(n)= h(0)*h(n-1) + h(1)*h(n-2) + ... + h(n-1)h(0) (其中n>=2),这是n阶递推关系;
还可以化简为1阶递推关系: 如h(n)=(4n-2)/(n+1)*h(n-1)(n>1) h(0)=1
该递推关系的解为:h(n)= C(2n,n)/(n+1)=P(2n,n)/(n+1)!=(2n)!/(n!*(n+1)1!) (n=1,2,3,...)
例题:
很多题目都是大数类型的,可以用java写!
HDU 1023 进出栈的方案数
HDU 1130 二分查找树的个数
http://acm.hdu.edu.cn/showproblem.php?pid=1130
import java.io. * ;
import java.util. * ;
import java.math. * ;
class Main {
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
int i;
BigInteger F[] = new BigInteger[ 105 ];
F[ 1 ] = BigInteger.valueOf( 1 );
for (i = 2 ;i < 101 ;i ++ )
{
// F[i] = F[i-1].add(BigInteger.valueOf((4*i-2) / (i+1)));
F[i] = F[i - 1 ].multiply(BigInteger.valueOf( 4 * i - 2 )).divide(BigInteger.valueOf(i + 1 ));
}
while (in.hasNext())
{
int n = in.nextInt();
System.out.println(F[n]);
}
}
}
普通的是C[n] = (n*4-2) * C[n-1] / (n + 1);这题是CC[n] = n! * C[n],换一下就是CC[n] = n * (n*4-2) * CC[n] / (n+1).