首页 > 其他分享 >[题解] <NOIP2017> 时间复杂度

[题解] <NOIP2017> 时间复杂度

时间:2024-04-11 18:33:19浏览次数:27  
标签:ch int 题解 复杂度 while 时间 loopNode getchar

[题解] NOIP2017 时间复杂度

题目描述

小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正确。
A++语言的循环结构如下:

F i x y
循环体
E

其中F \(i\) \(x\) \(y\) 表示新建变量 \(i\)(变量 \(i\) 不可与未被销毁的变量重名)并初始化为 \(x\) , 然后判断 \(i\) 和 \(y\) 的大小关系,若 \(i\) 小于等于 \(y\) 则进入循环,否则不进入。每次循环结束后 \(i\) 都会被修改成 \(i + 1\),一旦 \(i\) 大于 \(y\) 终止循环。
\(x\) 和 \(y\) 可以是正整数( \(x\) 和 \(y\) 的大小关系不定)或变量 \(n\) 。\(n\) 是一个表示数据规模的变量,在时间复杂度计算中需保留该变量而不能将其视为常数,该数远大于 100。
E 表示循环体结束。循环体结束时,这个循环体新建的变量也被销毁。
注:本题中为了书写方便,在描述复杂度时,使用大写英文字母 \(O\) 表示通常意义下 \(Θ\) 的概念。

输入格式

输入文件第一行一个正整数 \(t\) ,表示有 \(t( t \leq 10 )\) 个程序需要计算时间复杂度。 每个程序我们只需抽取其中 F \(i\) \(x\) \(y\) 和 E 即可计算时间复杂度。注意:循环结构允许嵌套。

接下来每个程序的第一行包含一个正整数 \(L\) 和一个字符串,\(L\) 代表程序行数,字符串表示这个程序的复杂度,O(1) 表示常数复杂度,O(n^w) 表示复杂度为 \(n^w\),其中 \(w\) 是一个小于 \(100\) 的正整数,输入保证复杂度只有 O(1) 和 O(n^w) 两种类型。

接下来 \(L\) 行代表程序中循环结构中的F \(i\) \(x\) \(y\)或者 E。

程序行若以F开头,表示进入一个循环,之后有空格分离的三个字符(串)\(i\) \(x\) \(y\), 其中 \(i\) 是一个小写字母(保证不为 \(n\) ),表示新建的变量名,\(x\) 和 \(y\) 可能是正整数或 \(n\) ,已知若为正整数则一定小于 \(100\) 。

程序行若以E开头,则表示循环体结束。

输出格式

输出文件共 \(t\) 行,对应输入的 \(t\) 个程序,每行输出 Yes 或 No 或者 ERR,若程序实际复杂度与输入给出的复杂度一致则输出 Yes,不一致则输出 No,若程序有语法错误(其中语法错误只有: ① F 和 E 不匹配 ②新建的变量与已经存在但未被销毁的变量重复两种情况),则输出 ERR。

注意:即使在程序不会执行的循环体中出现了语法错误也会编译错误,要输出 ERR。

题解

实际上一个A++程序的时间复杂度就是最高的带n循环嵌套层数。故在使用栈匹配循环头和循环结束语句的过程中模拟统计带n循环层数即可。

统计时需要注意两点,一是循环无法进入的情况,包括:

  1. \(x \geq y\)
  2. \(x = n, y\neq n\)

二是语法错误。语法错误一共有三种情况,包括:

  1. 出现了还未被释放的变量(使用vis数组记录)
  2. 在任意时刻E多过了F(判断栈空)
  3. 在最后时刻F多过了E(判断栈非空)

统计结束后,对比统计结果与题目给出的答案即可。

调试过程

对比初版代码,共发现以下错误:

  1. 没有考虑变量被回收的情况,没有重置vis标记
  2. \(x = y = n\)时可以进入循环但被我卡掉了,多加了不必要的判断
  3. 在还原tmp标记时没有考虑tmp修改时的条件(只有 \(able \neq 0\) 的时候才修改,所以只有able为0时才需要还原标记)

AC代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <string>
#include <stack>
#include <queue>
#define int long long
using namespace std;

bool vis[30]; // 用于标识字母是否出现过

struct LoopNode {
    char id;
    int x = 0, y = 0;
};

stack <LoopNode> st;

LoopNode readNode() {
    LoopNode loopNode;
    char ch = getchar();
    while (ch == ' ') ch = getchar();
    if (ch != 'n') {
        while (isdigit(ch)) {
            loopNode.x = (loopNode.x * 10) + ch - '0';
            ch = getchar();
        }
    } else {
        ch = getchar();
    }
    while (ch == ' ') ch = getchar();
    if (ch != 'n') {
        while (isdigit(ch)) {
            loopNode.y = (loopNode.y * 10) + ch - '0';
            ch = getchar();
        }
    } else {
        ch = getchar();
    }
    return loopNode;
}

int readTar() {
    int x = 0;
    char ch = getchar();
    while (ch != '(') ch = getchar();
    ch = getchar();
    if (ch == '1') {
        while (ch != ')') ch = getchar();
        return 0;
    }
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    while (ch != ')') ch = getchar();
    return x;
}

signed main() {
//    freopen("test.in", "r", stdin);
    int t;
    cin >> t;
    while (t--) {
        memset(vis, 0, sizeof vis);
        int n;
        cin >> n;
        int tar = readTar();
        bool ok = true;
        int able = 0; //记录当前有无不可进入的循环
        int tmp = 0; //记录当前栈中的有效循环数
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            char op;
            cin >> op;
            if (op == 'F') {
                char ch;
                cin >> ch;
                if (vis[ch - 'a']) ok = false;
                else vis[ch - 'a'] = true;
                LoopNode loopNode = readNode();
                loopNode.id = ch;
                st.push(loopNode);
                if ((!loopNode.x && loopNode.y) || (loopNode.x && loopNode.y && loopNode.x > loopNode.y)) //无法进入:n,1 2,1
                    able++;
                if (able) continue;
                if (loopNode.x && !loopNode.y) tmp++;
            } else if (op == 'E') {
                if (st.empty()) {
                    ok = false;
                } else {
                    ans = max(ans, tmp);
                    LoopNode loopNode = st.top();
                    if ((!loopNode.x && loopNode.y) || (loopNode.x && loopNode.y && loopNode.x > loopNode.y)) //无法进入:n,1 2,1
                        able--;
                    if (loopNode.x && !loopNode.y && able == 0) tmp--;
                    vis[loopNode.id - 'a'] = false;
                    st.pop();
                }
            }
        }
        if (!st.empty()) {
            ok = false;
            while (!st.empty()) st.pop();
        }
        if (ok) {
            if (ans == tar) cout << "Yes\n";
            else cout << "No\n";
        } else {
            cout << "ERR\n";
        }
    }
    return 0;
}

标签:ch,int,题解,复杂度,while,时间,loopNode,getchar
From: https://www.cnblogs.com/Floyd3265/p/18129847

相关文章

  • 复杂度来源------高可用
           软件系统复杂度的第二个来源高可用系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。       定义中的关键在于“无中断”,因为无论是单个硬件还是单个软件,都不可能做到无中断,硬件会出故障,软件会有bug;硬件会逐渐老化,软件......
  • 域名过期时间怎么计算
    域名是互联网上的重要标识,它帮助用户快速找到并访问特定的网站。为了维护域名的正常运行和避免不必要的麻烦,了解域名过期时间的计算和管理方式至关重要。首先我们先来了解一下域名的各种状态及域名注册与删除周期。 通常情况下,英文国际域名分为四种状态:活动期、注册商保留期......
  • CF617E XOR and Favorite Number 题解
    想了好久才明白zz来源?:莫队题单题目大意给定一个长度为\(n\)的序列\(a\),然后再给一个数字\(k\),再给出\(m\)组询问,每组询问给出一个区间,求这个区间里面有多少个子区间的异或值为\(k\)。\(1\len,m\le10^5\)\(0\lek,a_i\le10^6\)\(1\lel_i,r_i\len\)题......
  • java时间格式化
    使用DateFormat写项目时,pojo写了@JsonFormat(pattern="yyyy-MM-ddHH:mm:ss",timezone="Asia/Shanghai")privateDatecreateTime;,数据库用的是datetime格式直接用mybatis从数据库拿出来时间输出得到:WedApr1000:00:00CST2024解决方案在写impl时,不直接把从数......
  • 排序规则冲突问题解决
    --英文操作系统数据库恢复到中文版本操作系统的时候容易出现一下问题--无法解决equalto运算中"SQL_Latin1_General_CP1_CI_AS"和"Chinese_PRC_CI_AS"之间的排序规则冲突。--简单解决办法如下:指定排序规则COLLATESQL_Latin1_General_CP1_CI_AS或者Chinese_PRC_CI_AS......
  • Linux:修改系统时间
    学习自:Linux修改系统时间的两种方式-寻梦99-博客园 1、首先判断是要修改时间还是时区有的Linux系统时间错误,可能是因为时区不正确导致的:例如常见的时区是CST,但是当前系统时区为EDT,这时候只要把时区修改过来就好了。输入指令date,查看当前系统时间date WedAug1802......
  • java UTC时间格式化
    importjava.text.ParseException;importjava.text.SimpleDateFormat;importjava.util.Date;importjava.util.TimeZone;/***@author王睿*@date2019-01-2414:32*/publicclassTimeFormat{publicstaticvoidmain(String[]args)throwsParseExcept......
  • centos修改文件夹时间
    1、设置系统时间(能影响changetime)date -s "2010-10-10 10:10:10" 2、修改文件时间#当前目录下文件/文件夹(不能递归):touch -m -d "2010-10-10 10:10:10" *#递归修改当前目录下所有文件/文件夹3个时间戳(Access、Modify、Changetime):find ./ * -exec touch {} ......
  • (mysql)根据时间段获取连续日期,通过左连接便于每日统计
    代码:SELECTDATE_ADD(start_date,INTERVAL(a.a+(10*b.a)+(100*c.a))DAY)AS`date`FROM(SELECT'2024-02-24'ASstart_date,'2024-03-11'ASend_date)ASinputCROSSJOIN(SELECT0ASaUNIONALLSELECT1UNIONALLSELECT2UNIO......
  • 【题解】CF311E Biologist题解
    CF311EBiologist题解非常好的一道最小割。思路首先看到每一个位置又会有\(01\)两种情况,然后要满足一些要求,求最大收益,考虑类似于P4313文理分科和P1361小M的作物这种集合划分的建图方法,也就是要用最小割求解。由于我们要求的是最大收益,所以我们要先明确要最小化什么,......