第一次看到这道题的时候感觉 很有难度一脸懵逼 ,今天终于花了3个小时做完了,在这分享一下解法
题目
小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正确。
A++语言的循环结构如下:
F i x y
循环体
E
其中F i x y表示新建变量 ii(变量 ii 不可与未被销毁的变量重名)并初始化为 xx, 然后判断 ii 和 yy 的大小关系,若 ii 小于等于 yy 则进入循环,否则不进入。每次循环结束后 ii 都会被修改成 i +1i+1,一旦 ii 大于 yy 终止循环。
xx 和 yy 可以是正整数(xx 和 yy 的大小关系不定)或变量 nn。nn 是一个表示数据规模的变量,在时间复杂度计算中需保留该变量而不能将其视为常数,该数远大于 100100。
“E”表示循环体结束。循环体结束时,这个循环体新建的变量也被销毁。
注:本题中为了书写方便,在描述复杂度时,使用大写英文字母“O”表示通常意义下“Θ”的概念。
输入格式
输入文件第一行一个正整数 tt,表示有 tt(t \le 10t≤10)个程序需要计算时间复杂度。 每个程序我们只需抽取其中 F i x y和E即可计算时间复杂度。注意:循环结构 允许嵌套。
接下来每个程序的第一行包含一个正整数 LL 和一个字符串,LL 代表程序行数,字符 串表示这个程序的复杂度,O(1)表示常数复杂度,O(nw)表示复杂度为nwn
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开头,则表示循环体结束。
输出格式
输出文件共 tt 行,对应输入的 tt 个程序,每行输出Yes或No或者ERR(输出中不包含引号),若程序实际复杂度与输入给出的复杂度一致则输出Yes,不一致则输出No,若程序有语法错误(其中语法错误只有: ① F 和 E 不匹配 ②新建的变量与已经存在但未被销毁的变量重复两种情况),则输出ERR 。
注意:即使在程序不会执行的循环体中出现了语法错误也会编译错误,要输出 ERR。
输入输出样例
==输入 ==
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
说明/提示
【输入输出样例解释1】
第一个程序 ii 从 1 到 1 是常数复杂度。
第二个程序 xx 从 1 到 nn 是 nn 的一次方的复杂度。
第三个程序有一个 F 开启循环却没有 E 结束,语法错误。
第四个程序二重循环,nn 的平方的复杂度。
第五个程序两个一重循环,nn 的一次方的复杂度。
第六个程序第一重循环正常,但第二重循环开始即终止(因为nn远大于100,100大于4)。
第七个程序第一重循环无法进入,故为常数复杂度。
第八个程序第二重循环中的变量 xx 与第一重循环中的变量重复,出现语法错误②,输出 ERR。
思路
我在做这个题的时候选择了在线的做法,即边输入边运行。
输入输出—阅读题目我们会发现会在第一行读到要验证的组数,然后每组的第一个数字是行数。我们直接在刚开始和每组数据头读到nnnnn和n就可以解决循环次数的问题。
难点—要完成这个题首先要解决程序的嵌套问题 在这里我用了一个结构体栈来判断ERR和解决嵌套问题
解决过程
需要注意的问题 *时间复杂度和循环变量可能是两位数
我在代码里很详细的标注了
下面是AC代码:
#include<stdio.h>
#include<string.h>
struct node
{
char dm[200]; //在这里定义一个存字符串的结构体数组来做栈;
}list[200];
int main()
{
int nnnnn; //要测试的组数
char fzd[100],chac[100]; // fzd是读每个程序行数后的字符,,,chac用来存储循环变量
scanf("%d",&nnnnn);
int i,j,k,l,n,top,t,a,b,c,d,sjfzd,sjfzd0,q=0,flag,xx;
/* t,i,j,k,l 循环变量
n 每组数据行数
top 栈顶
a,b,c,d a,b是每个A++语言的循环次数
sjfzd,sjfzd0 第一个是读入的时间复杂度,第二个是程序算出来的时间复杂度
q=0 chac数组的初始化 他也是一个栈
flag,xx flag用来判断A++语言的循环是否结束 xx是循环嵌套未结束前的时间复杂度
*/
char s,x;
/*s用来读入F和E */
for(l=0;l<nnnnn;l++)
{
top=0;
sjfzd=0;
sjfzd0=0;
k=0; //每组数据开始前各种数据的初始化
q=0;
xx=0;
flag=0;
scanf("%d",&n); //读入本组测试数据行数
gets(fzd); //读入之后的字符用来提取时间复杂度
if(fzd[3]=='n')
{
sjfzd=fzd[5]-'0'; //读到n的时候时间复杂度的获取注意可能是两位数!!!
if(fzd[6]!=')')
{
sjfzd=sjfzd*10+(fzd[6]-'0');
}
}
else
{
sjfzd=0; //这里把O(1)的时间复杂度记为0
}
for(i=0;i<n;i++)
{
a=0;
b=0; //ab的初始化
scanf("%c",&s); //E F的获取
if(s=='E'&&!(l==nnnnn-1&&i==n-1)) getchar(); //吃E后边的回车
if(s=='F')
{
gets(list[top++].dm); //读入
chac[q++]=list[top-1].dm[1]; //获取循环变量
xx=0; //这里很关键,用来解决循环嵌套问题带来的时间复杂度归0
flag=1; //标记循环开始
}
if(s=='E'&&k==0)
{
if(top-1<0)
{
k=1; //当栈空时读到E 程序已经出错 标记K
continue;
}
top--; //退栈
for(c=1,j=3,d=0;j<strlen(list[top].dm);j++)
{
if(list[top].dm[j]==' ')
{
d=1;
c=1; //从list[top].dm[3]开始 下面的代码用来解决两位数的问题
continue;
}
if(list[top].dm[j]<'0'||list[top].dm[j]>'9')
{
if(d==1)
{
b=10000000;
break;
} //当读到非数字直接赋值10000000后面用来判断时间复杂度方便
else
{
a=10000000;
continue;
}
}
if(d==0)
{
a=a*c+(list[top].dm[j]-'0');
c*=10;
}
if(d==1) //两位数问题
{
b=b*c+(list[top].dm[j]-'0');
c*=10;
}
}
if(flag==1)
{
if(b-a>1000)
{
xx++; //当一次循环还未结束,让判断临时时间复杂度
}
if(b-a<0)
{
xx=0;
}
for(j=0;j<q;j++)
{
for(t=j+1;t<q;t++)
{
if(chac[j]==chac[t])
{
k=1; //判断循环变量重复问题
}
}
}
}
if(top==0)
{
flag=0;
q=0;
if(xx>sjfzd0) //栈空时将临时时间复杂度加入xjfzd0中并重置变量
sjfzd0=xx;
xx=0;
}
else
{
q--; //当栈未空读到E退栈
}
}
}
if(top!=0||k)
{
printf("ERR\n");
continue;
}
if(sjfzd==sjfzd0) //一组数据结束后的输出
{
printf("Yes\n");
}
else
{
printf("No\n");
}
}
return 0;
}