[NOIP2003]栈 题解(卡特兰数)

[NOIP2003]栈

Description

宁宁考虑的是这样一个问题:一个操作数序列,从1,2,一直到n(图示为1到3的情况),栈A的深度大于n。

现在可以进行两种操作:
1.将一个数,从操作数序列的头端移到栈的头端(对应数据结构栈的push操作)
2.将一个数,从栈的头端移到输出序列的尾端(对应数据结构栈的pop操作)
使用这两种操作,由一个操作数序列就可以得到一系列的输出序列;

你的程序将对给定的n,计算并输出由操作数序列1,2,…,n经过操作可能得到的输出序列的总数。

Solution

1.题目可将进栈记为0,出栈记为1,那么问题即为求由n个0和n个1组成的字符串数,条件是每个1出现前必须有一个对应的0出现;

2.那么可以推得方案数为总方案数减半,解与求01串的个数相同:n个0与n个1构成的序列方案数,使得任何一个前缀0的个数不少于1的个数;
做法是将0看做在坐标系中向右走一步,1看做向上走一步,则问题可化简为从原点到(n,n)所有路线中一直处于y=x之下(不越过直线y=x)的不同路径方案数,方案数即为对应n的卡特兰数;

3.因为没有要求取模,可以考虑使用高精度运算,输出n对应的卡特兰数即可;

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
int ans[100001]={},x=0; 
void mul(int n){
    for(int i=1;i<=ans[0];++i){
        ans[i]=ans[i]*n+x;
        x=ans[i]/10;
        ans[i]%=10;
    }
    while(x>0){
        ans[0]++;
        ans[ans[0]]=x%10;
        x/=10;
    }
}
void div(int n){
    int q=0;
    for(int i=ans[0];i>=1;--i)
     {
     	x=(ans[i]+q*10)%n;
     	ans[i]=(ans[i]+q*10)/n;
     	q=x;
     }
     while(ans[ans[0]]==0)ans[0]--;
}
int main(){
    ans[0]=ans[1]=1;
    int n;
    scanf("%d",&n);
    for(int i=n+2;i<=(n<<1);++i)mul(i);
    for(int i=2;i<=n;++i) div(i);
    for(int i=ans[0];i>0;--i)printf("%d",ans[i]);
    printf("
");
    return 0;
}

卡特兰数基础知识部分可以参考我的题解:http://www.cnblogs.com/COLIN-LIGHTNING/p/8450053.html

原文地址:https://www.cnblogs.com/COLIN-LIGHTNING/p/8481413.html