洛谷P3952[NOIP2017 提高组] 时间复杂度题解

题目描述:

小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正确。

A++语言的循环结构如下:

F i x y
    循环体
E

其中F i x y表示新建变量 𝑖(变量 𝑖不可与未被销毁的变量重名)并初始化为 𝑥, 然后判断 𝑖和 𝑦的大小关系,若 𝑖小于等于 𝑦则进入循环,否则不进入。每次循环结束后 𝑖都会被修改成 𝑖+1,一旦 𝑖大于 𝑦终止循环。

𝑥和 𝑦可以是正整数(𝑥 和 𝑦 的大小关系不定)或变量 𝑛。𝑛 是一个表示数据规模的变量,在时间复杂度计算中需保留该变量而不能将其视为常数,该数远大于 100。

E 表示循环体结束。循环体结束时,这个循环体新建的变量也被销毁。

注:本题中为了书写方便,在描述复杂度时,使用大写英文字母 O⁡表示通常意义下 Θ的概念。

输入格式:

输入文件第一行一个正整数 𝑡,表示有 𝑡(𝑡≤10)个程序需要计算时间复杂度。 每个程序我们只需抽取其中 F i x y 和 E 即可计算时间复杂度。注意:循环结构允许嵌套。

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

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

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

输出格式:

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

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

大致思路:模拟题干中的过程。利用stack记录最外层正在使用的变量,利用一个数组来记录这个循环的状态。(这里用0表示变量没有使用过,1表示会进入这个循环,且时间复杂度+1,2表示会进入这个循环,但时间复杂度不会增加,3表示无法进入这个循环)其他思路见注释。

注:时间复杂度+1当且仅当x != n且y == n。

代码:

#include<bits/stdc++.h>
using namespace std;
int to_integer(char num[])
{
	if(isalpha(num[0])) return 101;
//如果num!=n,那么必然有1<=n<=100.所以这里我用101来代替n 
	int res=0;
	for(int i=0;num[i];i++) res=res*10+num[i]-'0';
	return res;
}
int main()
{
	int T;
	scanf("%d",&T);
	getchar();//吃掉回车,因为getline会读入换行符。 
	string statement="";
	while(T--)
	{
		getline(cin,statement);
		int l,n=0;
		if(statement.find('^')!=string::npos)//如果时间复杂度不为O(1) 
			sscanf(statement.c_str(),"%d O(n^%d)",&l,&n);
		else sscanf(statement.c_str(),"%d O(1)",&l);
		//如果时间复杂度为O(1) 
		if(l&1)
		{
			while(l--) getline(cin,statement);//处理剩余输入 
			puts("ERR");
			continue;
		}
		//如果l为奇数,那么必然存在F和E不配对的情况,跳过这l次输入。 
		int state[128]={0};
//记录循环状态,0:未使用,1:时间复杂度+1,2:什么都不发生,3:这个循环进不去。 
		stack<char> stk;
		int blocks=0;//记录不能进入的循环的个数 
		bool is_legal=true;//判断这个语法是否正确 
		int com=0,res=0;
		//com用来记录实时时间复杂度(complexity),res用来更新答案 
		int i;
		for(i=0;i<l&&is_legal;i++)
		{
			getline(cin,statement);
			if(statement[0]=='F')//新建变量 
			{
				char var,xx[4],yy[4];
				sscanf(statement.c_str(),"F %c %s %s",&var,xx,yy);
				if(state[var])
				{
					is_legal=false;
					continue;
				}
//如果state值不为0,那么说明这个变量使用过,语法不正确。 
				stk.push(var);
				int x=to_integer(xx),y=to_integer(yy);
				if(x>y) state[var]=3,blocks++;
//如果x>y,那么这个循环进不去,记录状态并且blocks加一。 
				else if(x==101&&y==101||x!=101&&y!=101) state[var]=2;
//否则如果x==y==n或者x!=n且y!=n,记录状态 
				else
				{
					state[var]=1;
					if(!blocks) com++;
//如果前面没有进不去的循环,那么更新com值,否则就不用更新了,前面的进不去,这个就更不用说了。 
				}
			}
			else//销毁变量 
			{
				res=max(res,com);
				if(stk.empty()) 
				{
					is_legal=false;
					continue;
				}
//如果栈是空的,但是还有E,那么语法不正确。 
				char var=stk.top();
				stk.pop();
				if(state[var]==1)
				{
					if(!blocks) com--;
				}
//如果前面有进不去的循环,就不用恢复现场,原因同上。 
				else if(state[var]==3) blocks--;
//如果退出的是x>y的循环,那么blocks减一。 
				state[var]=0;//销毁变量,将此值重新记为0 
			}
		}
		if(stk.size()) is_legal=false;
//如果还有没销毁的变量,那么F和E不配对,语法不正确。 
		if(!is_legal)
		{
			for(;i<l;i++) getline(cin,statement);//同第27行 
			puts("ERR");
			continue;
		}
		if(res==n) puts("Yes");
		else puts("No");
	}
	return 0;
}

希望管理员审核通过,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值