05-Pop Sequence

3 篇文章 0 订阅
1 篇文章 0 订阅

写出来非常有成就感的一道题,更加深了对栈的理解,所以觉得值得记录一下~
算起来真正写程序加调试花了不过四十分钟,但是构建思路的时候花了足足两三个小时吧。
首先见到题目的第一眼脑海中浮现的就是最直接的也是最蠢的办法:把所有可能的出栈序列都列出来存起来,然后再一一匹配。不过这个想法一出来就被否定了…工作量太大不说,穷举出所有可能序列的算法就够杀死我好多脑细胞了。
然后关键来了,我又想到了【最大子列和问题】中的*【在线处理】思想,划重点。即每输入一个数据就根据一定的【规则】判断该数据是否为可能的出栈数,如果有一个不满足就可以直接判定该序列不可能。就像其他的在线处理一样,最难的地方想来就是找出这个【规则】*。
在受到了一些大佬思路启发、写写画画用掉了两张草稿纸以及薅掉了自己若干根头发后,终于被我想出来了….
如何根据上一个出栈数判断本出栈数是否合法呢?
第一个出栈只要不大于栈容量即可,按下不表。
剩下的每一个出栈数应当分为两种情况:
(1)、比上一个出栈数大;(2)、比上一个出栈数小。
对第一种情况而言,应当满足的是在栈容量范围内,即 出栈元素 < 容量 + 出栈次数 + 1,就是说最大可以为容量 + 出栈次数。在推导的时候借助了已入栈最大数,出栈元素 < 最大入栈数 + 容量剩余 + 1,其中 容量剩余 = 容量 - (最大入栈数 - 出栈次数),整理两个式子发现已入栈最大数被抵消了。最终就能得出:出栈元素 < 容量 + 出栈次数 + 1。其实也能理解,第一次的最大元素就是容量,每一次出栈就会空出一个位置让最大元素加一,所以最大元素是容量 + 出栈次数也就合情合理了。
对第二种情况而言,应当满足的是小于上一个出栈数的相近数,如果该数已经出栈那就再往前推。;例如前一个出栈数是7的话,本次出栈数要么就是6,如果6在之前已经出栈了,那就往前推,本次出栈数应该是5,如果5也已经出栈了处理方法相同。
把规则弄清楚之后就是用代码实现了。正如上面所说,代码实现倒不难。下面贴出代码。写的不好哈哈哈哈哈。

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

int main(int argc, const char * argv[]) {
    int Max;
    int Num;
    int K;
    scanf("%d %d %d", &Max, &Num, &K);
    int test[Num];
    int i, j, k;
    int flag = 0;
    int flag1;
    for (i = 0; i < K; i++) {
        flag1 = 1;
        for (j = 0; j < Num; j++) {
            scanf("%d", &test[j]);
            if (j == 0 && test[0] > Max){			//第一个出栈的元素
                for (k = 1; k < Num; k++)	
                    scanf("%d", &test[k]);
                break;
            }
            else if (j != 0) {						//非第一个出栈的元素
                if (test[j] > test[j - 1]) {		//比上一个出栈元素大
                    if (test[j] < Max + j + 1)		//判断是否大于容量
                        flag1++;				
                }
                else if (test[j] < test[j - 1]) {	//比上一个出栈元素小
                    int temp;
                    temp = test[j - 1] - 1;			//上一个出栈元素减一
                    for (k = 0; k < j; k++){		//对所有出栈元素进行遍历
                        if (test[k] == temp) {		//判断上一个出栈元素的下一个是否已经出栈
                            temp--;	
                            flag++;
                            k = -1;
                        }
                    }
                    if (test[j] == test[j - 1] - 1 - flag)	//上一个出栈元素减一减去上一个出栈元素
                        flag1++;
                    flag = 0;						//重置flag
                }
            }
        }
        if (flag1 == Num)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

PTA测试结果
——————分割线——————
再贴一种简单的算法
就是模拟出栈过程,逐个将1~M压入栈中。入栈过程中如果有和出栈元素一样的就出栈,再检查下一个出栈元素。
错误条件有二:

  1. 栈中总数大于栈容量;
    这个好理解,超出栈容量了
  2. 出栈元素小于栈顶元素。
    因为是按顺序入的栈,所以比栈顶元素小的元素肯定已经入过栈了,要么在栈中,要么已经出栈了。已经出栈了当然不能再出栈了,按下不表;如果在栈中的话根本不可能跨过栈顶元素出栈。所以这种情况错误
    代码实现如下

——————————————伪代码——————————————
	空栈;
	逐个分析出栈序列{
		如果出栈元素大于栈顶元素{
			继续入栈直至栈顶元素等于出栈元素;
				每次入栈时检查是否大于栈容量;
			出栈;
			}
		如果出栈元素等于栈顶元素;
			出栈;
		如果出栈元素小于栈顶元素;
			错误;
	}
	
——————————————代码————————————————
#include<iostream>
#include<vector>
#include<stack>
using namespace std;

const int MaxSize = 1005;

int main() {
	int M, N, K;
	int seq[MaxSize];
	stack<int> sta;
	int flag, num;
	cin >> M >> N >> K;

	for (int i = 0; i < K; i++) {
		//重置栈
		num = 0;
		flag = 1;
		while (!sta.empty())
			sta.pop();
		//输入
		for (int j = 0; j < N; j++)
			cin >> seq[j];
		//检测
		for (int j = 0; j < N; j++) {
			while (sta.empty())
				sta.push(++num);
			if (seq[j] > sta.top()) {
				while (seq[j] != sta.top()) {
					sta.push(++num);
					if (sta.size() > M)
						flag = 0;		//错误条件一
				}
				sta.pop();
			}
			else if (seq[j] == sta.top())
				sta.pop();
			else {
				flag = 0;				//错误条件二
				break;
			}
		}
		//输出结果
		if (flag == 0)
			cout << "NO" << endl;
		else
			cout << "YES"<<endl;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值