ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

关于出栈序列的解法总结及卡特兰数的学习(C语言)

2021-06-14 15:58:02  阅读:201  来源: 互联网

标签:出栈 int long C语言 二叉树 序列 2n 卡特兰


出栈次序

一个栈(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?

解法1——递归/记忆化搜索

  1. 考虑用一个二维数组f[i][j]模拟当前情况:i——进栈序列中还有i个待排的数,j——栈中有j个数,f[i][j]的表示当前i,j情况下有几种输出方案。
  2. 首先如果f[i][j]有值,直接调用即可(记忆化搜索,节省时间);
  3. 如果i=0,即序列全部入栈,只有一种输出方法,所以返回1;
  4. 考虑一般情况,有两种输出方案,先进一个再出,即加上f[i-1][j+1],(栈不空时,j>0,如果栈空只有第一种输出方案可行)直接出,即加上f[i][j-1]。

代码如下

long long f[MAX][MAX];
long long dfs(int i,int j)
{
	if(f[i][j])
		return f[i][j];
	if (i == 0)
		return 1;
	if(j>0)
		f[i][j] += dfs(i, j - 1);
	f[i][j] += dfs(i - 1, j + 1);
	return f[i][j];
}
int main()
{
	int n;
	scanf("%d", &n);
	printf("%lld", dfs(n, 0));
	return 0;
}

解法2——递推

  1. 首先重设一下f[i][j],f的含义不变,i为入栈数,j为出栈数;
  2. 我们知道f[0][j]=1,因为序列全部入栈,出栈次序是唯一的。
  3. 然后我们来考虑一下递归关系,如何得到f[i][j]?即怎么得到i个数入栈,j个数出栈的情况,只需要有i-1个数入栈,出栈j个,此时再入栈一个就是f[i][j];同理,可以是i个数入栈,j-1个出栈。所以我们得到递归关系f[i][j]=f[i-1][j]+f[i][j-1]。
  4. 但涉及到出栈必须考虑栈空的情况,什么时候栈空?i=j时,就只有f[i][j]=f[i-1][j]。

递归做法

long long f[MAX_N][MAX_N];
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=0;i<=n;i++)
	{
		f[0][i]=1;
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		{
			if(i==j)f[i][j]=f[i-1][j];
			else f[i][j]=f[i][j-1]+f[i-1][j];
		}
	}
	printf("%lld",f[n][n]);
	return 0;
}

解法3——卡特兰数

关于卡特兰数列的详细解释移步百度百科

  • 我们可以直接用的是它的四个递推公式

设h(n)为catalan数的第n项,令h(0)=1,h(1)=1,catalan数满足递推式 :
1)h(n)= h(0)*h(n-1)+h(1)*h(n-2) + … + h(n-1)h(0) (n≥2)
2)h(n)=h(n-1) * (4
n-2)/(n+1)
3)h(n)=C(2n,n)/(n+1) (n=0,1,2,…)
4)h(n)=C(2n,n) - C(2n,n-1) (n=0,1,2,…)

代码
都有递推关系了代码不是有手就行

//给出公式4作为参照
long long c[MAX][MAX];
int main(){
   int n;
   scanf("%d",&n);
   for(int i=1;i<=2*n;i++)
   {
   	c[i][0]=c[i][i]=1;
   	for(int j=1;j<i;j++)
   	{
   		c[i][j]=c[i-1][j]+c[i-1][j-1];
   	}
   }
   printf("%lld",c[2*n][n]-c[2*n][n-1]);
   return 0;
}

关键是分析为什么可以用卡特兰

  • 原理分析
  • 建立数组f。f[i]表示i个数的全部可能性。
    f[0] = 1, f[1] = 1; //当然只有一个
    设 x 为当前出栈序列的最后一个,则x有n种取值
    由于x是最后一个出栈的,所以可以将已经出栈的数分成两部分
    1.比x小
    2.比x大
    比x小的数有x-1个,所以这些数的全部出栈可能为f[x-1]
    比x大的数有n-x个,所以这些数的全部出栈可能为f[n-x]
    这两部分互相独立,所以根据乘法原理,一个x的取值能够得到的所有可能性为f[x-1] * f[n-x]
    另外,由于x有n个取值,所以
    ans = f[0]*f[n-1] + f[1]*f[n-2] + … + f[n-1]*f[0];
  • 或者这样分析
  • 我们把进栈设为状态‘1’,出栈设为状态‘0’。n个数的所有状态对应n个1和n个0组成的2n位二进制数。
    那么合法序列就是总序列-非法序列,总序列(由n次出栈n次入栈操作构成的序列数)为C(2n,n)。
    非法序列:由左而右扫描时,必然在某一奇数位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组成的排列。反过来,任何一个由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位数必对应一个不符合要求的数
    这就证明了不合要求的2n位数与n+1个0,n-1个1组成的排列一一对应。,为C(2n,n+1)
    所以有输出序列的总数目=c(2n,n)-c(2n,n-1)=c(2n,n)/(n+1)=h(n)。

我觉得这类题目可以总结为由特定顺序(二者匹配)的两种状态组成的排序数类问题

类似题目有

括号序列 :n 对括号,则有多少种 “括号匹配” 的括号序列

——左括号看作1,右括号看作0,和上题一样

二叉树n + 1 个叶子节点能够构成多少种形状不同的(国际)满二叉树
(国际)满二叉树定义:如果一棵二叉树的结点要么是叶子结点,要么它有两个子结点,这样的树就是满二叉树。

——形成满二叉树需要先向左扩展,再向右扩展,左右匹配,所以向左看作1,向右看作0,n+1个叶子结点有2n次扩展,还原为出入栈

买票找零:有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有10元钞票,剧院无其它钞票,问有多少种方法使得只要有10元的人买票,售票处就有5元的钞票找零

——拿5元的看作1,10元的看作0

所以要对这类题目有一定敏感性

有关题目练习可以去:


球迷购票问题
鸡蛋饼

标签:出栈,int,long,C语言,二叉树,序列,2n,卡特兰
来源: https://blog.csdn.net/Skye_r/article/details/117903638

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有