前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >卡特兰数总结

卡特兰数总结

作者头像
ShenduCC
发布2018-04-26 14:35:43
8790
发布2018-04-26 14:35:43
举报
文章被收录于专栏:算法修养算法修养

中间部分,小部分内容摘自百度百科 结尾部分,小部分内容摘自http://blog.sina.com.cn/u/1885661061

卡特兰数是组合数学中一个常出现在各种计数问题中出现的数列。这是一个很神奇的数列,就像黄金分割点在大自然中被神奇的应用。组合数学中也有很多问题是关于卡特兰数的。

卡特兰数的前几项是1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786,……..如果你发现测试数据里有这些数字,就表明,哎,这道题目可能是特斯拉数的应用。

卡特兰数的递推式:h(n)= h(0)*h(n-1)+h(1)*h(n-2) + … + h(n-1)h(0) (n>=2) 通项公式:h(n)=h(n-1)*(4*n-2)/(n+1); 递推式是怎么得来的,其实下面的应用中都可以证明。

第一个应用: 括号的匹配:给你2*n个括号,当然是n个左括号,n个右括号,这样才能匹配。问有多少种匹配方式,假设n=2,只有()(),(())两种。这个问题的答案就是特斯拉数,这里的理论证明放到第二个应用里说。 我当然可以用动态规划来解决这个问题,可以验证最终动态规划数组表示的就是卡特兰数。 首先我们把左括号当做1,右括号当作-1.我们设定dp[i][j]表示前i个括号,前缀和为j的数量。所以当前缀和为0时就是我们要求的解。状态转移方程很容易理解,dp[i][j]=dp[i-1][j+1]+dp[i-1][j-1],第i个是左括号自然加1,是右括号自然减1。

      memset(dp,0,sizeof(dp));
      dp[0][0]=1;
      for(int i=1;i<=2*n;i++)
      {
          for(int j=0;j<=n;j++)
          {
               if(j+1<=n) dp[i][j]=(dp[i][j]+dp[i-1][j+1];
               if(j-1>=0) dp[i][j]=(dp[i][j]+dp[i-1][j-1];
          }
      }

这里只有n个左括号,所以j最多有n。打印dp[2*n][0]发现dp[2*n][0]=h(n);

这里写图片描述
这里写图片描述

第二个应用: 出栈的次序:给你n个数,进栈的序列是1,2,3,4,…..n,有多少个不同的出栈次序。f(n)表示个数为n的序列的出栈次序种数,这里我们假定最后出栈的元素是k,在k入栈之前比k小值都出栈,所以有f(k-1)种,之后比k大的数在k之前出栈,所以有f(n-k)种,由于比k小和比k大的值入出栈是独立,所以可以用乘法原则,这里很重要,记住卡特兰数是组合数学的数列所以离不开乘法原理和加法原理,在一些应用中也要用到乘法原则,即f(n)=f(k-1)*f(n-k).由于k可以取1到n,根据加法原则,将k取不同的相加可以得到序列总数:f(n)=f(0)f(n-1)+f(1)f(n-2)+……+f(n-1)f(0)。你会发现和一开始的递推式是一样的。 这里把第一个应用,证明一下。因为第一个应用和第二个实际上一回事。

这里写图片描述
这里写图片描述

这是放在栈里的一组括号匹配,左括号就相当于n个进栈的数,那左括号什么时候出栈呢?就是当遇到右括号就出栈,右括号没有任何进栈出栈的操作,只是表示其相应的左括号什么时候出栈。所以2*n个括号匹配序列的种数,就相当于n个数出栈次序的种数。

这里给一道题目的连接,就是这个应用的解法。 http://acm.fzu.edu.cn/problem.php?pid=2098 这道题目改进了一下,限制了栈的高度m,否则就是卡特兰数的模板题了。 关于这道题目的解法,除了转换成括号匹配问题(那么前缀和最大就是m)。其实这也是一个简单的动态规划问题。dp[i][j]表示第i个数入栈,栈里还剩的元素。那么第i-1个数,栈里的元素可以是j-1(不出栈)也可以是比j大的数(出栈)。 状态转移方程:dp[i][j]=sum{dp[i-1][m….j-1]}; 给出一篇题解博客吧 http://blog.csdn.net/dacc123/article/details/50922580

第三个应用: 给定节点组成二叉树:给定n个点,能构成多少种不同的二叉树。

这里写图片描述
这里写图片描述

答案是h(n)种。证明很简单,一个二叉树由三部分组成根节点,左子树,右子树。左子树的节点个数可能是0,1,2,3,…n-1,相应的右子树的节点个数可能是n-1,n-2,n-3,……3,2,1,0.由于左子树和右子树是独立的,所以满足乘法原则。根据左子树的不同的节点数会产生不同的情况,满足加法原则所以f(n)=f(0)f(n-1)+f(1)f(n-2)+……+f(n-1)f(0)和一开始的递推式一样。

这里给一道关于这个应用的题目 http://acm.fzu.edu.cn/problem.php?pid=1064 这道理在这个理论上升级了,对二叉树进行排序,给你序数,让你输出这个二叉树。方法是递推,根据这颗树的排名,可以确定左子树的节点个数和右子树的节点个数。给一篇题解博客吧 http://blog.csdn.net/Dacc123/article/details/50922482

第四个应用: 凸包三角形划分:一个凸多边形种,通过若干条互不相交的对角线,把这个凸包划分成若干个三角形,求不同的划分的方案数。

这里写图片描述
这里写图片描述

答案就是h(n). 证明:在凸多边形上任意选一个顶点k,并选取任意两个不属于k的两个顶点,构成一个三角形。这个三角形把凸边形分成两个凸边形,由于两边的凸变形相互独立满足乘法原则,又由于k可以取1到n满足加法原则所以:f(n)=f(0)f(n-1)+f(1)f(n-2)+……+f(n-1)f(0)。

给一道题目关于凸包的应用, http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3537 这道题目虽然和卡特兰数无关,但是卡特兰数的思想,即把凸包切割成两个凸包,和最优三角划分的思想是一样的。关于凸包还有很多知识,比如凸包算法,给你一些列点,让你输出构成凸包的点。

其他应用: 在n*n的格子中,从左下角走到右上角,不能穿过对角线。只能向右或向上走

这里写图片描述
这里写图片描述

其实向右走相当于进栈,向左走相当于出栈,本质就是n个数出栈次序的问题

在圆上选择2*n个点,将这些点连接起来,所得到n条线段不相交

这里写图片描述
这里写图片描述

和凸包切割一个原理

用n个长方形填充一个高度为n的阶梯状图形的方法数

这里写图片描述
这里写图片描述

有n+1个叶子的满二叉树的个数

这里写图片描述
这里写图片描述

以上的问题答案都是卡特兰数,可见卡特兰数是多么的神奇。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-03-18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档