[Luogu P3952] [LOJ 2315] [NOIP tg 2017]时间复杂度

版权声明:欢迎转载蒟蒻博客,但请注明出处:blog.csdn.net/lpa20020220 https://blog.csdn.net/LPA20020220/article/details/82623828

洛谷传送门

LOJ传送门

题目描述

小明正在学习一种新的编程语言 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 10 )个程序需要计算时间复杂度。 每个程序我们只需抽取其中 F i x yE即可计算时间复杂度。注意:循环结构 允许嵌套。

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

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

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

输出格式:

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

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

输入输出样例

输入样例#1:

8
2 O(1)
F i 1 1
E
2 O(n^1)
F x 1 n
E
1 O(1)
F x 1 n
4 O(n^2)
F x 5 n
F y 10 n
E
E
4 O(n^2)
F x 9 n
E
F y 2 n
E
4 O(n^1)
F x 9 n
F y n 4
E
E
4 O(1)
F y n 4
F x 9 n
E
E
4 O(n^2)
F x 1 n
F x 1 10
E
E

输出样例#1:

Yes
Yes
ERR
Yes
No
Yes
Yes
ERR

说明

【输入输出样例解释1】

第一个程序 i 1 1 是常数复杂度。

第二个程序 x 1 n n 的一次方的复杂度。

第三个程序有一个 F 开启循环却没有 E 结束,语法错误。

第四个程序二重循环, n 的平方的复杂度。

第五个程序两个一重循环, n 的一次方的复杂度。

第六个程序第一重循环正常,但第二重循环开始即终止(因为 n 远大于 100 100 大于 4 )。

第七个程序第一重循环无法进入,故为常数复杂度。

第八个程序第二重循环中的变量 x 与第一重循环中的变量重复,出现语法错误②,输出 ERR

【数据规模与约定】

对于 30 % 的数据:不存在语法错误,数据保证小明给出的每个程序的前 L / 2 行一定为以 F 开头的语句,第 L / 2 + 1 行至第 L 行一定为以 E 开头的语句, L 10 ,若 x y 均 为整数, x 一定小于 y ,且只有 y 有可能为 n

对于 50 % 的数据:不存在语法错误, L 100 ,且若 x y 均为整数, x 一定小于 y , 且只有 y 有可能为 n

对于 70 % 的数据:不存在语法错误, L 100

对于 100 % 的数据: L 100

解题分析

接近一年前做的这玩意, 当时只搞了十几分, 这次1A了…

循环我们用栈来保存有关信息, 包括当前层复杂度, 所用变量。 如果有进不去的循环直接跳掉就好。

注意如果提前判了 E R R 一定要把剩下的操作读完。

详见代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cctype>
#include <string>
#include <map>
#include <cstdlib>
#include <algorithm>
#include <stack>
#define R register
#define IN inline
#define W while
#define gc getchar()
bool mp[30];
struct Message {bool typ; char x; int comp;};
struct Jump {int step, sig;};
std::stack <Message> st, jp;
IN int get_comp()
{
    char buf[30];
    R int ret = 0;
    scanf("%s", buf);
    if(buf[2] == '1') return 0;
    R int now = 4;
    W (isdigit(buf[now])) ret = ret * 10 + buf[now] - 48, ++now;
    return ret;
}

IN Message get_opt()//读取一次操作
{
    char buf[30];
    scanf("%s", buf);
    if(buf[0] == 'E') return {false, 'a', 0};
    Message ret; ret.typ = true;
    scanf("%s", buf); ret.x = buf[0];
    scanf("%s", buf);
    int typ1, typ2, rd1, rd2;
    if(buf[0] == 'n') typ1 = 1;
    else
    {
        R int now = 0; rd1 = typ1 =0;
        W (isdigit(buf[now])) rd1 = rd1 * 10 + buf[now] - 48, ++now;
    }
    scanf("%s", buf);
    if(buf[0] == 'n') typ2 = 1;
    else
    {
        R int now = 0; rd2 = typ2 = 0;
        W (isdigit(buf[now])) rd2 = rd2 * 10 + buf[now] - 48, ++now;
    }
    if((typ1 && typ2) || ((!(typ1 | typ2)) && rd1 < rd2)) {ret.comp = 0; return ret;}
    else if((!typ1) && typ2) {ret.comp = 1; return ret;}
    else {ret.comp = -1; return ret;}//-1表示是进不去的循环
}

IN void jump(R int tim) {W (tim--) get_opt();}//读取多余的操作

IN Jump ignore(R int lim, const Message &start)//跳过进不去的循环
{
    W (!jp.empty()) jp.pop(); Message now;
    jp.push(start); R int used;
    for (used = 1; used <= lim; ++used)
    {
        now = get_opt();
        if(!now.typ)
        {
            mp[jp.top().x - 'a'] = false; jp.pop();
            if(jp.empty()) return {used, 1};
        }
        else
        {
            if(mp[now.x - 'a']) return {used, 0};
            mp[now.x - 'a'] = true; jp.push(now);
        }
    }
    return {used, 0};
}

IN int solve(R int line, R int comp)
{
    int mx = 0, cnow = 0; Message now; Jump ret;
    std::memset(mp, false, sizeof(mp));
    W (!st.empty()) st.pop();
    for (R int i = 1; i <= line; ++i)
    {
        now = get_opt();
        if(!now.typ)
        {
            if(st.empty()) return jump(line - i), 0;
            now = st.top(), st.pop();
            cnow -= now.comp; mp[now.x - 'a'] = false;
        }
        else
        {
            if(mp[now.x - 'a']) return jump(line - i), 0;
            if(now.comp == -1)
            {
                mp[now.x - 'a'] = true;
                ret = ignore(line - i, now);
                if(ret.sig) i += ret.step;//注意加上在跳循环的时候读入的步数
                else return jump(line - i - ret.step), 0;
            }
            else
            {
                cnow += now.comp; mx = std::max(mx, cnow);
                mp[now.x - 'a'] = true;
                st.push(now);
            }
        }
    }
    if(st.size()) return 0;
    if(mx != comp) return 2;
    return 1;
}
int main(void)
{
    int a, b, c;
    int T;
    scanf("%d", &T);
    W (T--)
    {
        scanf("%d", &a);
        b = get_comp();
        c = solve(a, b);
        if(!c) puts("ERR");
        else if(c & 1) puts("Yes");
        else puts("No");
    }
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/82623828