Algorithm - 卡特兰数

1. 问题描述:
5个人有5元,另外5个人有10元,问有多少种10个人的排列方法使得是要有10元的人买票就能有5元找零。


2. 结论公式:
总数 = C(n, 2n) - C(n + 1, 2n)


3. 问题由来:
编号为1到n的n个元素,顺序的进入一个栈,则可能的出栈序列有多少种?


4. 另一个模型:
一个n×n的正方形网格,从左上角顶点到右下角顶点,只能向右走和向下走,且只能在此正方形网络的下三角范围内,问共有多少种走法。如果将向右走对应上述问题的进栈,向下走对应上述问题的出栈。


5. 等价问题:
n个1和n个0组成一个2n位的2进制数,要求从左到右扫描,1的累计数不小于0的累计数,试求满足这条件的数有多少?


6. 基本思路:
从C(n, 2n)中减去不符合要求的方案数即为所求。不合要求的数指的是从左而右扫描,出现0的累计数超过1的累计数的数。


7. 不合要求的特征:
不合要求的数的特征是从左而右扫描时,必然存在某一奇数2m+1位,其中共出现m+1个0的累计数,和m个1的累计数。此后的2(n-m)-1位上有n-m个1,n-m-1个0。如若把后面这部分2(n-m)-1位中的0与1相互替换,使之成为n-m个0,n-m-1个1,结果得1个由n+1个0和n-1个1组成的2n位数。因此,得出结论:一个不合要求的数对应于一个由n-1个0和n+1个1组成的一个排列。


8. 一一对应关系:
反过来,任何一个由n+1个0,n-1个1组成的2n位数,由于0的个数多2个,2n是偶数,故必在某一个奇数位上出现0的累计数超过1的累计数。同样在后面的部分,令0和1互换,使之成为由n个0和n个1组成的2n位数。即n+1个0和n-1个1组成的2n位数,必对应于一个不合要求的数。因此,得出结论:由n+1个0和n-1个1组成的2n位数,与由n个0和n个1组成的2n位数中从左向右扫描出现0的累计数超过1的累计数的数一一对应。


9. 举例:
例如:10100101
是由4个0和4个1组成的8位2进制数。但从左而右扫描在第5位出现0的累计数3超过1的累计数2,它对应于由3个1,5个0组成的10100010。反之:10100010对应于10100101。因而不合要求的2n位数与n+1个0,n-1个1组成的排列一一对应,因此,得出公式:
C(n, 2n) - C(n+1, 2n)


10. 另一种思维:
可以把这个问题描述为一个二元组,(n, 0)表示有n个元素等待进栈,0个元素已进栈,这相当于问题最初的状况。接着问题转化为(n-1, 1)。可以这么说(n, 0)=(n-1, 1)。
而对于(n-1, 1)则相当于(n-1,0)+(n-2,2)。(n-1, 0)表示栈中的一个元素出栈,(n-2, 2)表示又有一个元素入栈。
把问题一般话,则(n, m)的排列问题可以转化为(n, m-1) + (n-1, m+1)此时m>=1,因为必须栈中有元素才可以出栈。当m=0则(n, 0)的问题只能转化为(n-1, 1)。当问题为(0, m)时得到递归边界,这个问题的解是只有一种排列。


11. 求解例程:

int Catalan(int n, int m)
{
    /* 参数合法性检查 */
    if (n < 0 || m < 0)
    {
        return 0;
    }
    /* 递归边界 */
    if (0 == n)
    {
        return 1;
    }
    /* 对应于(n, 0)情形 */
    if (0 == m)
    {
        return Catalan(n - 1, 1);
    } 
    else // 对应于(n, m)情形
    {
        return Catalan(n, m - 1) + Catalan(n - 1, m + 1);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值