题目背景
NOIP2017 提高组 D1T2
题目描述
小明正在学习一种新的编程语言 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 y
和 E
即可计算时间复杂度。注意:循环结构允许嵌套。
接下来每个程序的第一行包含一个正整数 L 和一个字符串,L 代表程序行数,字符串表示这个程序的复杂度,O(1)
表示常数复杂度,O(n^w)
表示复杂度为 nw,其中 w 是一个小于 100 的正整数,输入保证复杂度只有 O(1)
和 O(n^w)
两种类型。
接下来 L 行代表程序中循环结构中的F i x y
或者 E
。 程序行若以F
开头,表示进入一个循环,之后有空格分离的三个字符(串)i x y
, 其中 i 是一个小写字母(保证不为nn),表示新建的变量名,x 和 y 可能是正整数或 nn ,已知若为正整数则一定小于 100。
程序行若以E
开头,则表示循环体结束。
输出格式
输出文件共 tt 行,对应输入的 t 个程序,每行输出 Yes
或 No
或者 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
说明/提示
【输入输出样例解释 11】
第一个程序 ii 从 11 到 11 是常数复杂度。
第二个程序 xx 从 11 到 nn 是 nn 的一次方的复杂度。
第三个程序有一个 F
开启循环却没有 E
结束,语法错误。
第四个程序二重循环,nn 的平方的复杂度。
第五个程序两个一重循环,nn 的一次方的复杂度。
第六个程序第一重循环正常,但第二重循环开始即终止(因为 nn 远大于 100100,100100 大于 44)。
第七个程序第一重循环无法进入,故为常数复杂度。
第八个程序第二重循环中的变量 xx 与第一重循环中的变量重复,出现语法错误②,输出 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。
解题思路见注释
#include <bits/stdc++.h>
using namespace std;
int n,m,num;
string str,s1,s2,s3;
char s;
void solve(){
cin>>m>>str;
//给定的时间复杂度的计算
int w;
if(str.size()>4)
for(int i=4;i<str.size()-1;i++) w=w*10+str[i]-'0';
else w=0;
//在线处理语句
int cnt1=0,cnt2=0,maxx=0; //顺序n循环次数 倒序n循环次数 实际的复杂度
stack<int> st1; //记录具体的循环 0为常数循环 1为正序 -1为倒序 int vis[300]={0}; //记录变量是否出现过
stack<char> st2; //记录具体出现的变量
int vis[300]={0}; //标记变量是否出现过
int flag=1; //标记是否存在语法错误
while(m--){
cin>>s; //先读 F /E
if(s=='F'){
cin>>s1>>s2>>s3;
//判断变量合法性
if(vis[s1[0]-'a']) flag=0; //若已出现过 则语法错误
else{
st2.push(s1[0]);
vis[s1[0]-'a']=1;
}
if(!flag) continue;
//判断循环类型
if(s2!="n"){
if(s3=="n") st1.push(1),cnt1++;
else if(stod(s2)<=stod(s3)) st1.push(0);
else st1.push(-1),cnt2++;
}
else{
if(s3=="n") st1.push(0);
else st1.push(-1),cnt2++;
}
}
else{
if(st1.empty()) flag=0; //栈空 匹配失败
else{
//更新循环语句复杂度
if(cnt2==0) maxx=max(maxx,cnt1);
//若存在倒序语句 之后的循环都无法就进入
//所以只有在pop掉所有的倒序语句后才能更新计算出的复杂度
//只有在无倒序语句时 才能计算复杂度
if(st1.top()>=0) cnt1-=st1.top();
else cnt2--; //倒序循环直接pop掉 自减即可
st1.pop();
//更新变量的栈
vis[st2.top()-'a']=0;
st2.pop();
}
}
}
if(!st1.empty()) flag=0; //数量匹配不对等 标记错误
if(!flag) cout<<"ERR"<<endl;
else if(maxx==w) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
int main(){
cin>>n;
while(n--) solve();
return 0;
}