刷题Day12卡特兰数

前言

 开头以为是个暴力搜索的过程,本来想问递归;但是回想一下,感觉递归来做好像时间复杂度会很高,因为有重叠的子问题在里面,然后就想起来用动态规划来做。推导完DP表达式以后发现这是卡特兰数。
 题目来源:P1044 [NOIP2003 普及组] 栈
在这里插入图片描述
在这里插入图片描述
题意理解:
 本题的题目重点不是栈,而是借着栈来表达一种做法。初始一共分为三个部分,输入序列,中间的栈以及输出序列。输入序列上是1-n这n个数字的排列,然后这n个数字可以进入入栈操作;入栈后的数字可以进行出栈到输出序列上的操作。当输入序列和栈皆为空的时候,输出序列就形成了1-n这n个数字的一个新的排列。现在题目要求输入序列数字总数为n的时候输出序列能够形成排列的总个数。
题目解答:
 乍一看的递归搜索,因为对应这样的状态转移关系:(1)当栈不空的时候,若输入序列为空则只能进行数字出栈操作,反之既可以进行入栈操作又可以进行出栈操作(2)当栈空的时候,若输入序列不空则只能进行入栈操作,反之则进行结算。理论上利用递归来做没问题;但是当输入数据规模n很大的时候,空间和事件开销都很大。
 注意到,假设n=4,则对于输入序列1 2 3 4,初始1入栈以后,很容易发现,如果1此时出栈,那么问题转化为2 3 4求输出序列个数,而这又和对1 2 3求输出序列个数一致。而当1不出栈,而是等2入栈,由于栈的特性,1一定在2之后出现,此时,若2出栈,1也出栈(即1紧跟2之后)则问题也跟求1 2的输出序列一样了…这样,记 a i a_i ai为输入序列为1,2,3,…i时输出序列的总个数,那么对于 a n a_n an,假设 a 1 , a 2 , . . . a n − 1 a_1,a_2,...a_{n-1} a1,a2,...an1已知,同时假设 a 0 a_0 a0为1(至于为啥 a 0 a_0 a0是1可以自己想,主要还是可以统一表达式),那么当输出序列中以1开头,则一共有 1 ∗ a n − 1 = a 0 ∗ a n − 1 1*a_{n-1}=a_0*a_{n-1} 1an1=a0an1种不同情况;当输出序列以2,3被1截断做划分,则前者只有一个2,对应 a 1 a_1 a1种情况,后者从3到n一共n-2个数,对应输出序列有 a n − 2 a_{n-2} an2种,一共是 a 1 ∗ a n − 2 a_1*a_{n-2} a1an2;当输出序列以3,4为截断,即1在2,3都到输出序列后再到输出序列上,则前者有 a 2 a_2 a2中情况,后者为 a n − 3 a_{n-3} an3种情况,一共是 a 2 ∗ a n − 3 种 a_2*a_{n-3}种 a2an3;… …;直到 2 … n全都到输出序列后再将1出栈,则有 a n − 1 ∗ 1 = a n − 1 ∗ a 0 a_{n-1}*1=a_{n-1}*a_0 an11=an1a0中情况,将它们都加起来则得到如下递推表达式:
     a n = a 0 ∗ a n − 1 + a 1 ∗ a n − 2 + a 2 ∗ a n − 3 + . . . + a 1 ∗ a n − 2 + a 0 ∗ a n − 1 a_n=a_0*a_{n-1}+a_1*a_{n-2}+a_2*a_{n-3}+...+a_1*a_{n-2}+a_0*a_{n-1} an=a0an1+a1an2+a2an3+...+a1an2+a0an1
 这样初始化 a 0 a_0 a0然后一直DP求到 a n a_n an就可以了。下面给出我的AC代码:

#include <stdio.h>
#include <stdlib.h>


int main(int argc, char *argv[]) {
	int n,i,j;
	scanf("%d",&n);
	int a[n+1];
	for(i=0;i<=n;i++){
		if(i==0)
		  a[i]=1;
		else
		  a[i]=0;
	}
	for(i=1;i<=n;i++){
		for(j=0;j<=i-1;j++)
		  a[i]+=a[j]*a[i-1-j];
	}
	printf("%d",a[n]);
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值