(有一个月没有写简书了......)
(这次终于开始拿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 记录变量的次数)
注意!题目最后边还有一句话
全部代码 (快捷复制:按住鼠标左键选择你要的代码然后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