[NOIP2017]时间复杂度+关于为啥这题你想不到用栈的反思

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序,于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正确。 A++ 语言的循环结构如下:
F i x y
循环体
E
然后判断 i 和 y 的大小关系,若 i 小于等于 y 则进入循环,否则不进入。每次循环结束后i都会被修改成 i +1,一旦 i 大于 y 终止循环。 x 和 y 可以是正整数(x 和 y 的大小关系不定)或变量 n。n 是一个表示数据规模的变量,在时间复杂度计算中需保留该变量而不能将其视为常数,该数远大于 100。 `E`表示循环体结束。循环体结束时,这个循环体新建的变量也被销毁。
注:本题中为了书写方便,在描述复杂度时,使用大写英文字母 O 表示通常意义下 Θ\ThetaΘ 的概念

可以说是非常繁琐的一道题了,但我们只考虑重点:为啥栈那么好用你却不用?

首先,用栈实现这道题的好处都有啥?栈可以类比为咱电脑网站的页面(其原理其实本身就是栈),程序浏览页面时突然发现有个东西需要处理,所需的具体信息是从前执行过的,但 程序本身是木有记忆哒呀!咋办呢?由此我们引入栈让序看上去“有记忆”。加上循环体本来就是层层套层层,F离哪个E近那他俩就是一对,这些看起来就是栈啊……

为了更好地理解栈在这里的作用,来看个例子:假设栈内存变量名和操作前的循环层数(设目前循环层数为all):

F x 1 n       读入F,栈内(顶->底):x 0(all=0)操作后all=1,纪录此最大循环次数
F y 1 n       读入F,栈内(顶->底):y 1(all=1);x 0,操作后all=2,纪录此最大循环次数
E                读入E,关键来啦!令此时的all=栈顶循环次数!此时成功告诉程序:没有了该                   层循环,则此时循环层数应该是多少(是1)all=1
E                读入E,同上,此时循环层数应该是0,all=0
F                到这里发现,上面的循环层数完全不会影响到这,因为利用栈 对 记录的循环层数           进行操作,非常巧妙地避开了这个天坑。程序操作同上。
E

栈另外的妙用:

1. 可以通过栈的空与否判断F或E是否多了。(读E时栈空了,说明没F与他对应;最后判断的时候栈不为空,说明没有E与F对应),对此可以将栈理解为幼儿园里的小朋友,只有对应的家长(E)来了才能领走,家长领不到或有小朋友滞留就出错啦。

2. 当退出循环时,你的变量名就又能用啦!这就是为毛栈还要存变量名:当读到E,就把这层用到的变量的标记给去掉。

如果有需要用到多次的操作,你就写个函数吧还省事。

#include<bits/stdc++.h>
using namespace std;
int n;
string k;
int str_to_int(string a) {
	int ans=0;
	for(int i=0; i<a.length(); i++)
		ans=ans*10+a[i]-'0';
	return ans;
}
struct oppo {
	char na;
	int s;
	oppo(char na,int s):na(na),s(s) {};
};
stack<oppo> v;
bool MAP[205];
void work() {
	int sj=0,ans=0,all=0,flag=0;
	while(v.size()) v.pop();
	memset(MAP,0,sizeof(MAP));

	cin>>n>>k;
	if(k!="O(1)") {
		for(int i=0; i<k.length(); i++)//不是O(1)就记录n的次数 
			if('0'<=k[i]&&k[i]<='9')
				sj=sj*10+k[i]-'0';
	}
	char t,a;
	string b,c;
	for(int i=1; i<=n; i++) {
		cin>>t;//输入字符串 
		if(t=='F') {//如果是F 
			cin>>a>>b>>c;
			int bb,cc;
			if(b=="n") bb=2020;//循环初值 
			else bb=str_to_int(b);

			if(c=="n") cc=2020;//循环末值 
			else cc=str_to_int(c);

			if(MAP[a]) flag=1;//出现过就er(flag记录er 
			MAP[a]=1;

			if(cc-bb>100) {//可以进入循环的情况且为n次 
				v.push(oppo(a,all));//之前的变量以及循环层数入栈 
				all++;//循环多加了一层,*n 
				ans=max(all,ans);//记录循环出现过的最大值 
			} else {//非n次循环的情况 
				if(bb>cc) {//如果开始值>终止值 
					v.push(oppo(a,all));//之前的变量以及循环层数入栈 
					all=-10000;//循环层数-inf?????????? 
				} else {//常数O(1) 
					v.push(oppo(a,all));//之前的变量以及循环层数入栈
				}
			}
		} else {// E的情况 
			if(v.size()) {//如果栈不空 
				all=v.top().s;//记录栈顶的循环层数 
				MAP[v.top().na]=0;//将栈顶的变量名置0 !退出循环就可重复利用了! 
				v.pop();//移除栈顶元素 
			} else//如果栈空 
				flag=1;//E多了 
		}
	}
	if(flag||v.size()) puts("ERR");
	else if(ans==sj) puts("Yes");
	else puts("No");
	return ;
}
int T;
int main() {
	cin>>T;//
	while(T--)
		work();
	return 0;
}
//

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值