模拟-NOIP 2017 Day1 T2 时间复杂度 complexity - 题解

@豪哒哒哒HaoDaDaDa

(有一个月没有写简书了......)
(这次终于开始拿Markdown写了,富文本可以走开了)
说说为什么我要写这道题...
因为...dalao们写的都是两百多行的代码看不懂
于是我决定自己写一篇...
也是第一次拿markdown写
错误连篇请原谅
原谅我可好 我傲慢的青春

题目索引 [NOIP 2017 Day1 T2 时间复杂度]

LOJ.ac #2315
loj老卡,可能我家网不好

题面如下

都给我老老实实看题 ,[不想看题的戳这][1]

题目描述

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

F i x y
    循环体
E

然后判断 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 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,其中 iii 是一个小写字母(保证不为 n ),表示新建的变量名,x 和 y 可能是正整数或 n ,已知若为正整数则一定小于 100。 程序行若以 E开头,则表示循环体结束。

样例
样例输入(好心的我已经tab好了)
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
样例输出
Yes 
Yes 
ERR 
Yes 
No 
Yes 
Yes 
ERR
样例说明
第一个程序 i 从1 到 1 是常数复杂度。
第二个程序 x 从 1 到 n 是 n 的一次方的复杂度。
第三个程序有一个 F 开启循环却没有E结束,语法错误。
第四个程序二重循环,n的平方的复杂度。
第五个程序两个一重循环,n 的一次方的复杂度。
第六个程序第一重循环正常,但第二重循环开始即终止(因为 n 远大于 100,100 大于 4)。
第七个程序第一重循环无法进入,故为常数复杂度。
第八个程序第二重循环中的变量 x 与第一重循环中的变量重复,出现语法错误②,输出 ERR。
数据范围与提示
1.对于 30%的数据:不存在语法错误,数据保证小明给出的每个程序的前 L/2 行一定为以 F 开头的语句,第L/2+1 行至第 L 行一定为以 E 开头的语句,L≤10,若 x,y均为整数,x 一定小于 y,且只有 y 有可能为 n。

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

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

4.对于 100% 的数据:t≤10,L≤100。

......(话说题目好长真的懒得看)

题解...

题目分析

很明显这是一道不需要智商但是打起来贼恶心的模拟题

我们先来看一看题发现它的语言是A++而且只有F循环结构

根据我们的常识知道这里多半要压栈因为我们需要知道当前的F循环是哪一个

然后判断一下有没有语法错误就好啦!!!

好了说正经的,五步写完

1.数据准备

int T,l,on,Fnum,Maxon;//T程序数,l程序行数,on当前复杂度,Fnum当前程序F-E个数,Maxon当前程序最大复杂度 
string On,Ans;//输入,答案 
bool isError;//当前程序错误开关 
const int INF=-99999;//进不去的循环

2.准备一个函数,检查当前复杂度与期望复杂度是否一致

注意: 期望 O(1)即为O(n^0),w=0
(string) On 为期望复杂度(输入)
然后机智的我们可以发现在
(string) On 中期望复杂度只需要 取出(On[4]~On[On.length()-1])所表示数就好了
最后 return r==w;

bool checkRight(int w){// 检查复杂度是否一致
    if(w==0&&On=="O(1)") return 1; 
    else {
        int r=0;
        for(int i=4;i<On.length()-1;i++) {//这个for实际上就是从(string)On里取出数字来 
            r*=10;
            r+=On[i]-'0';
        }
        return r==w;
    }
}

3.再准备一个函数,获取某个F循环的复杂度

注意:一个F循环只有可能存在三种复杂度:0 , 1 , INF
INF意思看上边
这里我们简单记 F(s,e)为从 s一直循环加1到 e 的复杂度
设const_为某常数(据题const_<=100)

when s=e=n 时 , 循环可以进入一次故 F(n,n)=0;
when s=const_,e=n 时 , 循环需要走n次(因为n远大于const_) ,F(const_,n)=1
when s=n,e=const_ 时 , 循环无法进入 (因为const_远小于n) ,F(n,const_)=INF负无穷大
3个判断后,s,e均为常数
when const_1<=const_2 时 ,F(const_1,const_2)=0;
when const1>const_2 时 , 循环无法进入,F(const_1,const_2)=INF负无穷大

code↓↓↓
同理,两个for从string中取数,不详讲

int getOn(string x,string y){//获取复杂度 
    if(x!="n"&&y=="n") return 1;//从 常数 到 n :复杂度为 1 
    if(x=="n"&&y!="n") return INF;//从 n到常数 :由于无法进入故认为其复杂度为负无穷  依据题目 -99999 已经足够 
    if(x=="n"&&y=="n") return 0;//从 n到 n相当于 常数 到 常数 复杂度为 0 
    
    //x y 均为数字 ,则只要判断循环能否进入即可,能进入 复杂度为0 , 不能进入 复杂度则为 负无穷(INF) 
    int xx=0,yy=0;
    for(int i=0;i<x.length();i++){
        xx*=10;
        xx+=x[i]-'0';
    }
    for(int i=0;i<y.length();i++){
        yy*=10;
        yy+=y[i]-'0';
    }
    return yy>=xx?0:INF;//yy>=xx 常数级别  
}

4.入读数据

(读入完后的工作以 work()省略)
map<Object , Object > map_name用法详细见 link[... ]

scanf("%d",&T);
    while(T--){//小明写了n个 A++ 程序 
        map<string ,int >map_times;//关联容器 将变量名与次数关联,可与下边胡map_on合并 
        map<string ,int >map_on;//储存变量的复杂度 
        stack<string >st;// top() 表示当前所在胡循环 
        isError=false;//错误开关关闭 
        Fnum=Maxon=on=0;//F-E记次清零 最大复杂度,当前复杂的清零 
        cin>>l>>On;//获取每个程序初始数据 行数与期望复杂度 
        string F,i,x,y;//准备 循环输入数据 
        while(l--){//程序行数 
            cin>>F;
            if(F=="F"){//首字母检测 F则开始循环 E则退出 
                cin>>i>>x>>y;
                work_1();
            }else{//首字母为 E
                work_2();
            }
        }
        cout<<Ans<<endl;//输出答案 
    }

5.work()

好吧这道题恶心也就在ERR的时候
分析一下 ERR有这么几种情况(重复请忽视)

1. 程序行数L为奇数,则F,E肯定不匹配 (F,E个数不匹配)
2. F,E 出入栈不匹配
3. F,E 个数不匹配 (用Fnum记录,输入F则++,输入E则--)
4. F循环变量名字重复 (用map<string ,int >map_times 记录变量的次数)

注意!题目最后边还有一句话

attention.png
全部代码 (快捷复制:按住鼠标左键选择你要的代码然后Ctrl+c)
#include <bits/stdc++.h>
using namespace std;
int T,l,on,Fnum,Maxon;//T程序数,l程序行数,on当前复杂度,Fnum当前程序F-E个数,Maxon当前程序最大复杂度 
string On,Ans;//输入,答案 
bool isError;//当前程序错误开关 
const int INF=-99999;
const bool DEBUG=false; 

bool checkRight(int w){// 检查复杂度是否一置 
    if(w==0&&On=="O(1)") return 1; 
    else {
        int r=0;
        for(int i=4;i<On.length()-1;i++) {//这个for实际上就是从(string)On里取出数字来 
            r*=10;
            r+=On[i]-'0';
        }
        return r==w;
    }
}
int getOn(string x,string y){//获取复杂度 
    if(x!="n"&&y=="n") return 1;//从 常数 到 n :复杂度为 1 
    if(x=="n"&&y!="n") return INF;//从 n到常数 :由于无法进入故认为其复杂度为负无穷  依据题目 -99999 已经足够 
    if(x=="n"&&y=="n") return 0;//从 n到 n相当于 常数 到 常数 复杂度为 0 
    
    //x y 均为数字 ,则只要判断循环能否进入即可,能进入 复杂度为0 , 不能进入 复杂度则为 负无穷(INF) 
    int xx=0,yy=0;
    for(int i=0;i<x.length();i++){
        xx*=10;
        xx+=x[i]-'0';
    }
    for(int i=0;i<y.length();i++){
        yy*=10;
        yy+=y[i]-'0';
    }
    return yy>=xx?0:INF;//yy>=xx 常数级别  
}
int main(){
//  freopen("complexity.in","r",stdin);
//  freopen("complexity.out","w",stdout);
    scanf("%d",&T);
    while(T--){//小明写了n个 A++ 程序 
        map<string ,int >map_times;//关联容器 将变量名与次数关联,可与下边胡map_on合并 
        map<string ,int >map_on;//储存变量的复杂度 
        stack<string >st;// top() 表示当前所在胡循环 
        isError=false;//错误开关关闭 
        Fnum=Maxon=on=0;//F-E记次清零 最大复杂度,当前复杂的清零 
        cin>>l>>On;//获取每个程序初始数据 行数与期望复杂度 
        if(l%2) isError=true;//如果程序行数是个奇数,那么F-E一定无法一一对应 
        string F,i,x,y;//准备 循环输入数据 
        while(l--){
            cin>>F;
            if(F=="F"){//首字母检测 F则开始循环 E则退出 
                Fnum++;//F 计次 
                cin>>i>>x>>y;
                map_times[i]++;//当前程序中的 循环i的 次数 
                st.push(i);//将 i循环 压栈 ,栈顶 i即为当前所在循环  
                if(isError||map_times[i]>1){//如果已经error了,则没必要计算复杂度 
                    isError=true;//如果循环i再次出现 >>ERR 
                    continue;
                }
                map_on[i]=getOn(x,y);//获取 循环i 的复杂度 
                on+=map_on[i];//复杂度累加前边嵌套的 
                Maxon=max(on,Maxon);//最大值迭代 
            }else{//首字母为 E 则终止 st.top() 表示的循环 
                if(isError)continue;// error 跳过 
                Fnum--;//F 个数 减1 
                if(st.empty())continue;//栈为空 当前执行已不在任何循环内 
                on-=map_on[st.top()];//当前复杂度 减去 当前循环st.top() 的复杂度 
                map_times[st.top()]--;//循环i出现的次数 减1 
                st.pop();//当前循环 退栈 
            }
        }
        if(Fnum!=0||isError){//F-E个数不相等 , ERR输出 
            cout<<"ERR"<<endl;
            continue;
        }
        Ans=checkRight(Maxon)?"Yes":"No";//检查答案是否正确 
        cout<<Ans<<endl;//输出答案 
    }
    return 0;
}

最后更新 2018.10.31 8:51 By HaoDaDaDa

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342