zoukankan      html  css  js  c++  java
  • 【説明する】栈

    引例:

    codevs 1086 栈 

    时间限制: 1 s
     空间限制: 128000 KB
     题目等级 : 黄金 Gold
     
    题目描述 Description

    栈是计算机中经典的数据结构,简单的说,栈就是限制在一端进行插入删除操作的线性表。

    栈有两种最重要的操作,即pop(从栈顶弹出一个元素)和push(将一个元素进栈)。

    栈的重要性不言自明,任何一门数据结构的课程都会介绍栈。宁宁同学在复习栈的基本概念时,想到了一个书上没有讲过的问题,而他自己无法给出答案,所以需要你的帮忙

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

    现在可以进行两种操作,

    1.将一个数,从操作数序列的头端移到栈的头端(对应数据结构栈的push操作)

    2. 将一个数,从栈的头端移到输出序列的尾端(对应数据结构栈的pop操作)

    使用这两种操作,由一个操作数序列就可以得到一系列的输出序列,下图所示为由1 2 3生成序列2 3 1的过程。(原始状态如上图所示) 。

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

    输入描述 Input Description

    输入文件只含一个整数n(1≤n≤18)

    输出描述 Output Description

    输出文件只有一行,即可能输出序列的总数目

    样例输入 Sample Input

    3

    样例输出 Sample Output

    5

    思路:

    出栈次序

    题目大体上就是说一个栈(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?

    常规分析

    首先,我们设f(n)=序列个数为n的出栈序列种数。(我们假定,最后出栈的元素为k,显然,k取不同值时的情况是相互独立的,也就是求出每种k最后出栈的情况数后可用加法原则,由于k最后出栈,因此,在k入栈之前,比k小的值均出栈,此处情况有f(k-1)种,而之后比k大的值入栈,且都在k之前出栈,因此有f(n-k)种方式,由于比k小和比k大的值入栈出栈情况是相互独立的,此处可用乘法原则,f(n-k)*f(k-1)种,求和便是Catalan递归式。

    首次出空之前第一个出栈的序数k将1~n的序列分成两个序列,其中一个是1~k-1,序列个数为k-1,另外一个是k+1~n,序列个数是n-k。

    此时,我们若把k视为确定一个序数,那么根据乘法原理,f(n)的问题就等价于——序列个数为k-1的出栈序列种数乘以序列个数为n - 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)。

    看到此处,再看看卡特兰数的递推式,答案不言而喻,即为f(n)=h(n)= C(2n,n)/(n+1)= c(2n,n)-c(2n,n+1)(n=0,1,2,……)。

    最后,令f(0)=1,f(1)=1。

    非常规分析

    对于每一个数来说,必须进栈一次、出栈一次。我们把进栈设为状态‘1’,出栈设为状态‘0’。n个数的所有状态对应n个1和n个0组成的2n位二进制数。由于等待入栈的操作数按照1‥n的顺序排列、入栈的操作数b大于等于出栈的操作数a(a≤b),因此输出序列的总数目=由左而右扫描由n个1和n个0组成的2n位二进制数,1的累计数不小于0的累计数的方案种数。

    在2n位二进制数中填入n个1的方案数为c(2n,n),不填1的其余n位自动填0。从中减去不符合要求(由左而右扫描,0的累计数大于1的累计数)的方案数即为所求。

    不符合要求的数的特征是由左而右扫描时,必然在某一奇数位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+1)。

    代码

     1 #include <cstdio>
     2 #include <iostream>
     3 
     4 using namespace std;
     5 
     6 int n,dp[30];
     7 
     8 int main()
     9 {
    10     cin>>n;
    11     dp[1]=1,dp[0]=1;
    12     for(int i=2;i<=n;i++)
    13     {
    14         dp[i]=dp[i-1]*(4*i-2.0)/(i+1.0);
    15     }
    16     cout<<dp[n];
    17     return 0;
    18 }

    如果运气好也是错,那我倒愿意错上加错!

    ❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀

  • 相关阅读:
    树的基本概念
    bean的生命周期
    bean的创建过程--doCreateBean
    bean的创建过程--doGetBean
    SpringBoot自动装配解析
    [论文理解] Good Semi-supervised Learning That Requires a Bad GAN
    Ubuntu 环境安装 opencv 3.2 步骤和问题记录
    Linux 环境使用 lsof 命令查询端口占用
    Ubuntu 安装不同版本的 gcc/g++ 编译器
    [持续更新] 安全能力成长计划
  • 原文地址:https://www.cnblogs.com/zxqxwnngztxx/p/6736011.html
Copyright © 2011-2022 走看看