洛谷P1044栈——递推做法

题目背景

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

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

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

题目描述

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

现在可以进行两种操作,

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

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

(原始状态如上图所示)

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

输入格式

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

输出格式

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

输入输出样例

输入 #1复制

3

输出 #1复制

5

这道题目算是有一些难度了,我的做法有点像动态规划,不过没学过动态规划的小伙伴也不用担心,这道题只学过地推也可以理解。

这里先来聊一下栈,栈是一种数据结构,数据先进后出(FILO)fisrt in last out,这个就有点像子弹上膛一样,先上在弹夹上的子弹只能等到上方的子弹先弹出才能再将最下面的弹出。这里就不过多赘述了,现在来讲一下我是如何解决这道题目的。

先来给大家画个图举个例子。

先假设放入栈的元素只有1。stack(m,n)是我用来表示入栈数字的数量和出栈数字的数量,m为入栈数字数量,n为数字出栈的数量。这幅图片为还没有入栈时的样子,所以为stack(0 , 0)

 现在我们将1 插入栈中

 

 此时没有元素可以再往栈中插入了,所以现在只能弹出,所以一个元素只有一种插入的方法。

那么现在我们有两种元素,1 ,2。

现在栈中没有元素,所以我们只能先插入一个元素进去

 现在选择出现了我们可以先将1 弹出栈 然后再向栈中插入2

 此时入栈数和出栈数刚好相等,此时我们知道此时栈为空,所以此时我们只能将2入栈

现在能插入的元素都插完了,所以只能开始出栈

 此时入栈数和出栈数正好相等,并且所有元素均已插入且弹出

现在我们来列举刚才的另一种可能当1入栈后,我们不着急将1先弹出,先将2压入栈中

 此时没有元素可以再插入了,所以我们应该开始弹出,

 

 就变成了这样。这里我就只举两个例子。现在从这两个例子中我们可以得到一些公式(划重点)

首先stack[ m ][ n ] = stack[m - 1][ n ] + stack[ m ][ n - 1]  这个式子表明栈现在的入栈数为m出栈数为n这样的状态是由前一个stack入栈或者前一个stack出栈来的。

其次当m == n时 ,此时栈为空,所以我们只能是由上一个栈出栈得来的,也就是stack[ m ][ n ] = stack[ m ][n - 1]

再就是当我们栈中装满了所有元素的时候,现在只有一种(从上而下)的出栈方式,也就是    stack [ m ][ 0 ] = 1(只有一种出栈方案)

现在开始coding吧

	int num;
	cin >> num;
const int N = 20;
int stack[N][N];

首先读入数据,并且创建二维数组用来表示目前栈的状态

	for (int i = 0; i <= num; i++) {
		stack[i][0] = 1;
	}

使用之前推导的公式,当所有元素入栈的时候,此时只有一种出栈方式

	for (int i = 1; i <= num; i++) {
		for (int j = 1; j <= i; j++) {
			if (i == j) {
				stack[i][j] = stack[i][j - 1];
			}
			else {
				stack[i][j] = stack[i - 1][j] + stack[i][j - 1];
			}
		}
	}

当i == j时 ,也是入栈数和出栈数相同时,此时栈中元素为空,因此只能执行入栈操作。除此之外,就有之前两种状态的得来的,最后输出最后的二维数组的最后一位,就是最后得到这个状态的答案。

现在,我把所有代码放在后面,有需要的同学可以用来借鉴一下

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 20;
int stack[N][N];
int main() {
	int num;
	cin >> num;
	for (int i = 0; i <= num; i++) {
		stack[i][0] = 1;
	}
	for (int i = 1; i <= num; i++) {
		for (int j = 1; j <= i; j++) {
			if (i == j) {
				stack[i][j] = stack[i][j - 1];
			}
			else {
				stack[i][j] = stack[i - 1][j] + stack[i][j - 1];
			}
		}
	}
	cout << stack[num][num];
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值