zoukankan      html  css  js  c++  java
  • 出栈序列的可能性判定

    问题说明

    PAT1051:给定 stack 的容量,给定数据的入栈顺序:从 1 开始的正整数序列,在允许随机的出栈操作的情况下,要求判断某出栈序列是否可能。

    比如,告知 stack 容量为 5,入栈序列的最大值为 7。有两个序列需要判断合理性:

    • 1 2 3 4 5 6 7: 这个序列是可能的,只需每次入栈时都做出栈操作。
    • 3 2 1 7 5 6 4: 这个序列是不可能的,其中前半部分3 2 1是合法的,先将1 2 3顺序入栈,然后三次执行出栈操作。而之后的7 5 6则是不可能的。

    要完成判定过程,常规思路是直接使用的 stack 数据结构模拟出栈序列做操作,然后判定是否会触犯条件。但考虑到 PAT1051 中时间限制只有 10ms,虽然常规方法是线性的,似乎也无法保障(事实证明是错误的,用常规方法也能在 PAT 上 AC),我想到从序列本身的特性入手,找规律,于是有了一种效率更高的判定逻辑。

    常规思路

    直接使用出栈序列指导 stack 模拟操作。判定条件有两条:

    • 1.栈中数据量不超过栈的容量。
    • 2.出栈只能从栈顶取,不应该出现从固定的堆栈中取出其他数据的情况。

    算法描述如下:

    用游标记录当前已知压栈的最大数据 cur。如果新的读入数据 tmp(即出栈序列中的某数据)大于 cur,则将 cur 到 tmp 之间的数据顺序压入栈中,更新 cur 并执行检查 1;如果新的读入数据 tmp 小于 cur,则一定是直接出栈获得的,执行检查 2。

    如果能顺利完成就是合理的,如果操作过程违背了一些规则,则判定为不合理。C++实现代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    #include<stdio.h>
    #include<stack>
    using namespace::std;
    int m, n, k, tmp, cur;
    bool flag;
    stack<int> s;
    int main()
    {
        scanf("%d %d %d", &m, &n, &k);
        while(k --) {
            flag = true;
            cur = 1;
            s.push(1);
            for (int i = 0; i != n; ++ i) {
                scanf("%d", &tmp);
                if (tmp > cur) {
                    for (int j = cur + 1; j <= tmp; ++ j)
                        s.push(j);
                    if (s.size() > m) flag = false;
                    cur = tmp;
                }else {
                    if (s.top() != tmp)
                        flag = false;
                }
                s.pop();
            }
    
            if (flag) printf("YES
    ");
            else printf("NO
    ");
        }
    }
    

    更高效的判定逻辑

    实际上,在 PAT1051 的环境下,由于入栈序列数据由小到大排列非常特殊,要通过出栈序列判定可能性是存在简便思路的。

    对比分析题中 Sample 给出的序列,结合上面提到的两条冲突条件入手分析:

    • 1.栈中数据量不超过栈的容量:

      只有在入栈时,才会需要考虑栈中数据是否超量。出栈序列中的每个数,都以为着在出栈操作之前,它刚入栈,那么当它入栈的时候能否判定是否超过栈容量呢?可以的,(当前的出栈数值 - 已经执行过的出栈操作数量)就是当前栈中元素的数量。

    • 2.出栈只能从栈顶取,不应该出现从固定的堆栈中取出其他数据的情况。

      根据观察分析发现,当某数据 m 出栈之后,比 m 小的数据如果在 m 之后出栈的,它们所组成的序列本身需要保持从大到小的顺序排列。距离如3 2 1 7 5 6 4这个序列,在7之后有5 6 4这个子序列,它们都大于7,但却没有保持一个递减的顺序,不合法。

    C++实现代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    #include<stdio.h>
    int m, n, k;
    int max, min, tmp;
    bool flag;
    int main()
    {
        scanf("%d %d %d", &m, &n, &k);
        while(k --) {
            flag = true;
            max = 0;
            min = 1001;
    
            for (int i = 0; i != n; ++ i) {
                scanf("%d", &tmp);
                if (tmp > max) {
                    if (tmp - i > m) flag = false;
                    else max = min= tmp;
                } else {
                    if ( tmp > min) flag = false;
                    else min = tmp;
                }
            }
    
            if (flag) printf("YES
    ");
            else printf("NO
    ");
        }
    }
    

    总结

    在我的理解之中,经典的算法、数据结构是在面对编程问题的解决过程中所抽象出的通用模型。而生活是多变的,并不像考试卷一样简单的套用数学题所能解决,很多情况下,编程问题也是如此。那么除了这些经典的方法外,认真分析条件,并进行针对性的优化甚至重新设计就非常重要了。这里仅仅是一个小实践。

  • 相关阅读:
    报告论文:数字图像噪音处理研究
    报告论文:汽车GPS调度系统车载终端设计技术方案
    疾病研究:我是一名34岁的进行性肌营养不良症患者
    08年最热门七大技术和最紧缺的IT人才
    C#序列化
    网络游戏的数据管理
    2008年3月Google占美国搜索请求量市场份额达67.3%
    C#反转字符串效率最高的方法
    何时应该使用极限编程
    几种字符串反转方法效率比较
  • 原文地址:https://www.cnblogs.com/biaobiaoqi/p/3288725.html
Copyright © 2011-2022 走看看