P3952 时间复杂度

P3952 时间复杂度

题面:P3952 [NOIP2017 提高组] 时间复杂度

这个世界上为什么会有模拟这种东西啊

因为做的时候没审好题导致心态直接爆炸

以为会出现

F i 1 n
F a 1 i
F b a n
E
E
E

这种东西

后来再读题发现边界字母只会出现 \(n\)

感谢出题人不杀之恩

题解

首先考虑什么情况下会出现 ERR

  1. 出现了使用过的变量

    可以开一个 map<char,bool> 来记录这个变量是否使用过

  2. FE 不匹配

    用栈来模拟,如果 E 的时候栈为空或者程序结束后栈不为空则不合法

接着开始计算复杂度

一共有四种情况

  1. 常量常量 :常数复杂度,比较两个常量 \(a_1,a_2\)的大小即可,若 \(a_1\leq a_2\) ,那么可以累加,否则不能进入循环
  2. 常量\(n\) :由于常量值小于 \(100\)\(n\) 远大于 \(100\) ,可以累加 \(O(N)\) 的复杂度
  3. \(n\)常量无法进入循环
  4. \(n\)\(n\) :常数复杂度

其中,仅有 \(O(N)\) 复杂度可以累加

对于不能进入循环的情况,不能简单地跳过,因为中间可能会出现 ERR

那么可以用栈来记录该循环体内部是否可以记录复杂度

又因为之前正好也需要一个栈来记录循环嵌套情况(仅通过栈内元素数量判断)

那么就可以将这个栈定义为 stack<bool> ,若栈顶为 true 则表示不能累加复杂度,向栈内 push 一个 true (保证内部循环也不累加复杂度)就可以继续了

另外,一层循环退出后要及时删除该层循环所使用的变量,这里用 stack<char> 来记录当前层循环的变量名

一个程序的复杂度是最深的循环层数,所以还要取 \(\max\)

代码

愿天堂没有模拟

#include<bits/stdc++.h>
using namespace std;

const int maxL = 200;

stack<bool> blc;//以后的循环加不加复杂度
stack<char> tp;//目前循环的变量名(删变量时用)
map<char,bool> vis;//判断变量是否用过

int T;
int L;
char Tc[maxL];//小明给出的时间复杂度
char opt_;//操作符,F或者E
bool flg1;//判断是否ERR
char bl,ff[maxL],tt[maxL];
int fzd;//记录当前复杂度

//多组数据,记得初始化
inline void INIT(){
	for(int i = 'a';i<='z';i++) vis[i] = 0;
	fzd = 0;
	while(!blc.empty()) blc.pop();
	while(!tp.empty()) tp.pop();
	memset(Tc,0,sizeof(Tc));
	flg1 = false;
}

//将char数组转为数字,用于提取循环边界的数字
inline int ToInt(char S[]){
	int res = 0;
	int pd = 1;
	int Ll = strlen(S);
	for(int i = 0;i<Ll;i++){
		res += (S[Ll-i-1] - '0') * pd;
		pd *= 10;
	}
	return res;
}

//判断能否累加复杂度(如果栈空或者栈顶为false则可以累加)
//写麻烦了,其实可以 return blc.empty() || !blc.top() 的
inline bool gtc(){
	return !(!blc.empty() && blc.top());
}

//从小明给出的复杂度中提取O(N^x)的x
inline int gtC(char S[]){
	int cnt = 0;
	int Ll = strlen(S);
	char C[maxL];
	bool nt1 = false;
	for(int i = 0;i<Ll;i++){
		if(S[i] == 'n') nt1 = true;//如果出现了n,那么不是O(1) 复杂度
		if(isdigit(S[i])) C[cnt++] = S[i];
	}
	C[cnt] = '\0';
	return nt1 ? ToInt(C) : 0;
}

int main(){
//	freopen("P3952_6.in","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%d",&T);
	while(T--){
		INIT();
		scanf("%d %s",&L,Tc);
		int maf = 0;
		for(int i = 1;i<=L;i++){
			scanf("\n%c",&opt_);
			if(opt_ == 'F'){
				scanf(" %c%s%s",&bl,ff,tt);//这个读入真的要好小心才不会错
				if(vis[bl]) flg1 = true; //变量重复,ERR(但因为多组数据,只能接着读入程序)
				tp.push(bl);//记录当前层循环用的变量名
				vis[bl] = 1;//记录该变量名已经使用
                
				if(!gtc()){
					blc.push(true);//如果当前层循环不能累加复杂度,那么其内部的循环也不能累加
					continue;
				}
				if(ff[0] == 'n'){
					if(tt[0] == 'n'){
						//F n to n 常数复杂度,不累加
						blc.push(false);
					}else{
						//F n to C 无法进入循环
						blc.push(true);
					}
				}else{
					if(tt[0] == 'n'){
						//F C to n O(N)复杂度
						if(gtc())fzd++;//如果可以累加就累加
						maf = max(maf,fzd);//求最大复杂度
						blc.push(false);
					}else{
						//F C to C 常数复杂度/无法进入循环
						int t1 = ToInt(ff);
						int t2 = ToInt(tt);
						blc.push(t1 > t2);
					}
				}
			}else{
				if(blc.empty()){
					flg1 = true;//栈空却需要退出循环,ERR
					continue;
				}else{
					blc.pop();
					char c = tp.top();tp.pop();//删除变量名
					vis[c] = 0;
					fzd--;//退出循环后复杂度变小
					fzd = max(fzd,0);
				}
			}
		}
		if(flg1 || !blc.empty()){
			printf("ERR\n");
			continue;
		}else{
			int r1,r2;
			r1 = gtC(Tc);
			r2 = max(maf,fzd);
			printf("%s\n",r1 == r2 ? "Yes" : "No");
		}
	}
	return 0;
}
posted @ 2022-05-05 15:50  Burnling  阅读(49)  评论(0编辑  收藏  举报