zoukankan      html  css  js  c++  java
  • Catalan数

    Catalan 数

    引入

    Problem:

    具有n个节点的二叉树的形态有多少种?

    分析:

    因为二叉树定义具有递归性,左子树有i个节点,那么右子树有n-i-1个节点,根据计数原理:

    [fleft ( n ight )=sum_{i=0}^{n-1}fleft ( i ight )cdot fleft ( n-i-1 ight ),fleft ( 0 ight )=1,fleft ( 1 ight )=1 ]


    Catalan数

    这里的:

    [fleft ( n ight )=sum_{i=0}^{n-1}fleft ( i ight )cdot fleft ( n-i-1 ight ) ]

    就叫做卡特兰数,该数列的前若干项是:

    1,2,5,14,42,132,429,1430....

    所以增长速度是非常快的。

    常见的Catalan数表达式

    1、递推式:

    [fleft ( n ight )=sum_{i=0}^{n-1}fleft ( i ight )cdot fleft ( n-i-1 ight ),fleft ( 0 ight )=1 ]

    2、另类递推式:

    [fleft ( n ight )=frac{(4n-2)fleft ( n-1 ight )}{n+1} ]

    3、通向式(重要,常用):

    [fleft ( n ight )=frac{Cegin{matrix} n\ 2n end{matrix}}{n+1} ]

    4、通向式2:

    [fleft ( n ight )=Cegin{matrix} n\ 2n end{matrix} -C egin{matrix} n-1\ 2n end{matrix} ]

    利用通向式求Catalan(n) Code

    #define ll long long
    ll Catalan(ll n){
    	ll ans=1;
    	for(ll i=n+1;i<=2*n;i++){
    		 ans=ans*i/(i-n);
    		 printf("%d %d %lld
    ",i,i-n,ans);
    	}
    	return ans/(n+1);
    }
    

    应用:

    •Cn表示n个节点不同形态的二叉树个数。

    •Cn表示n的入栈序列对应的合法出栈序列的个数。(luogu P1044)

    ​ 变式:

    ​ •n个0和n个1,构造一个长度为2n的序列,使得序列的任意前缀中1的个数不 少于0的个数,这样的序列有多少种?

    ​ •n个矩阵相乘,用括号改边运算顺序,有多少种?(ps:n个矩阵相乘需要n-1对括号,再增加是无意义的)

    ​ •游乐园门票1元一张,每人限购一张。现在有10个小朋友排队购票,其中5个小朋友每人只有1元的钞票一张,另5个小朋友每人只有2元的钞票一张,售票员没有准备零钱。问:有多少种排队方法,使售票可以正常进行下去?

    •凸多边形分割成三角形方案数

    图像法

    n·n的矩阵,每次只能往右或往上走1个单位,问从(0,0)走到n·n,且路线一直处于y=x之下的方法总数是多少?

    只需要n步的向右和n步的向上就能到达(n,n)。为了不跨越y=x,需任意时刻向右的次数>=向上的次数,显然答案就是卡特兰数

    扩展:几乎所有的卡特兰数的问题都可以用这样的折线法方式解答。比如入栈映射为向右,出栈映射成向上,任意时刻入栈次数>=出栈


    例题-luogu P2532 [AHOI2012]树屋阶梯

    Problem

    Problem

    Problem

    Problem

    分析

    卡特兰数,注意N的取值范围,要用高精度

    Code

    #include <cstdio>
    #include <iostream>
    #define ll long long
    using namespace std;
    const int maxn=10005;	
    struct highprecc{
    	int l,a[maxn];
    	void init(){l=1;a[1]=1;}
    	void out(){for(int i=l;i>=1;i--) printf("%d",a[i]);}
    	highprecc operator * (const ll b) const{
    		highprecc c;
    		for(int i=1;i<=l;i++) c.a[i]=a[i]*b;
    		for(int i=2;i<=l;i++){
    			c.a[i]+=c.a[i-1]/10;
    			c.a[i-1]%=10;
    		}
    		c.l=l;
    		while(c.a[c.l]>10){
    			c.a[c.l+1]=c.a[c.l]/10;
    			c.a[c.l]%=10;
    			c.l++;
    		}
    		return c;
    	}
    	highprecc operator / (const ll b) const{
    		highprecc c;
    		ll k=l,g=0;
    		for(int i=l;i>0;i--){
    			g=g*10+a[i];
    			c.a[i]=g/b;
    			g%=b;
    		}
    		while(k>1 && c.a[k]==0) k--;
    		c.l=k;
    		return c;
    	}
    };	
    highprecc f(ll n){
    	highprecc ans;ans.init();
    	for(ll i=n+1;i<=2*n;i++) ans=ans*i/(i-n);
    	return ans/(n+1);
    }
    int main(){
        ll n;scanf("%lld",&n);
        f(n).out();
        return 0;
    }
    
    

  • 相关阅读:
    Python学习
    我的计算机网络复习笔记(第一章)
    理解DES算法
    彻底理解RSA加密算法
    扩展欧几里得算法求模的乘法逆元
    python的deque(双向)队列详解
    对于暴力枚举的一些优化方法的题解
    python中的多(liu)元(mang)交换 ,赋值
    python定义函数后跟->的意义
    直接暴力做分糖问题
  • 原文地址:https://www.cnblogs.com/saitoasuka/p/10329023.html
Copyright © 2011-2022 走看看