NOIP 2017 Day1 T2 时间复杂度 complexity - 模拟题 题解

作者@豪哒哒哒HaoDaDaDa
转载自简书@豪哒哒哒HaoDaDaDa-简书-NOIP 2017 Day1 T2 时间复杂度

(有一个月没有写简书了…)
(这次终于开始拿Markdown写了,富文本可以走开了)
说说为什么我要写这道题…
因为…dalao们写的都是两百多行的代码看不懂
于是我决定自己写一篇…
也是第一次拿markdown写
错误连篇请原谅
原谅我可好 我傲慢的青春

题目索引 [NOIP 2017 Day1 T2 时间复杂度]

LOJ.ac #2315
loj老卡,可能我家网不好

题面如下

都给我老老实实看题 ,[不想看题的戳这][1]

题目描述

给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序,于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正确。 A++ 语言的循环结构如下:

F i x y
    循环体
E

然后判断 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) 表示复杂度为 n^w,其中 w 是一个小于 100 的正整数(输入中不包含引号),输入保证复杂度只有 O(1) 和 O(n^w) 两种类型。

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

样例
样例输入~~(好心的我已经tab好了)~~
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
样例说明
第一个程序 i 从1 到 1 是常数复杂度。
第二个程序 x 从 1 到 n 是 n 的一次方的复杂度。
第三个程序有一个 F 开启循环却没有E结束,语法错误。
第四个程序二重循环,n的平方的复杂度。
第五个程序两个一重循环,n 的一次方的复杂度。
第六个程序第一重循环正常,但第二重循环开始即终止(因为 n 远大于 100,100 大于 4)。
第七个程序第一重循环无法进入,故为常数复杂度。
第八个程序第二重循环中的变量 x 与第一重循环中的变量重复,出现语法错误②,输出 ERR。
数据范围与提示
1.对于 30%的数据:不存在语法错误,数据保证小明给出的每个程序的前 L/2 行一定为以 F 开头的语句,第L/2+1 行至第 L 行一定为以 E 开头的语句,L≤10,若 x,y均为整数,x 一定小于 y,且只有 y 有可能为 n。

2.对于 50% 的数据:不存在语法错误,L≤100,且若 x,y均为整数,x 一定小于 y,且只有 y 有可能为 n。

3.对于 70% 的数据:不存在语法错误,L≤100。

4.对于 100% 的数据:t≤10,L≤100。

(话说题目好长真的懒得看)

题解…

题目分析

很明显这是一道不需要智商但是打起来贼恶心的模拟题

我们先来看一看题发现它的语言是A++而且只有F循环结构

根据我们的常识知道这里多半要压栈因为我们需要知道当前的F循环是哪一个

然后判断一下有没有语法错误就好啦!!!

好了说正经的,五步写完

1.数据准备
int T,l,on,Fnum,Maxon;//T程序数,l程序行数,on当前复杂度,Fnum当前程序F-E个数,Maxon当前程序最大复杂度 
string On,Ans;//输入,答案 
bool isError;//当前程序错误开关 
const int INF=-99999;//进不去的循环
2.准备一个函数,检查当前复杂度与期望复杂度是否一致

注意: 期望 O(1)即为O(n^0),w=0
(string) On 为期望复杂度(输入)
然后机智的我们可以发现在
(string) On 中期望复杂度只需要 取出(On[4]~On[On.length()-1])所表示数就好了
最后 return r==w;

bool checkRight(int w){// 检查复杂度是否一置 
	if(w==0&&On=="O(1)") return 1; 
	else {
		int r=0;
		for(int i=4;i<On.length()-1;i++) {//这个for实际上就是从(string)On里取出数字来 
			r*=10;
			r+=On[i]-'0';
		}
		return r==w;
	}
}
3.再准备一个函数,获取某个F循环的复杂度

注意:一个F循环只有可能存在三种复杂度:0 , 1 , INF
INF意思看上边
这里我们简单记 F(s,e)为从 s一直循环加1到 e 的复杂度
设const_为某常数(据题const_<=100)

when s=e=n 时 , 循环可以进入一次故 F(n,n)=0;
when s=const_,e=n 时 , 循环需要走n次(因为n远大于const_) ,F(const_,n)=1
when s=n,e=const_ 时 , 循环无法进入 (因为const_远小于n) ,F(n,const_)=INF负无穷大
3个判断后,s,e均为常数
when const_1<=const_2 时 ,F(const_1,const_2)=0;
when const1>const_2 时 , 循环无法进入,F(const_1,const_2)=INF负无穷大

code↓↓↓
同理,两个for从string中取数,不详讲

int getOn(string x,string y){//获取复杂度 
    if(x!="n"&&y=="n") return 1;//从 常数 到 n :复杂度为 1 
    if(x=="n"&&y!="n") return INF;//从 n到常数 :由于无法进入故认为其复杂度为负无穷  依据题目 -99999 已经足够 
    if(x=="n"&&y=="n") return 0;//从 n到 n相当于 常数 到 常数 复杂度为 0 
    
    //x y 均为数字 ,则只要判断循环能否进入即可,能进入 复杂度为0 , 不能进入 复杂度则为 负无穷(INF) 
    int xx=0,yy=0;
    for(int i=0;i<x.length();i++){
        xx*=10;
        xx+=x[i]-'0';
    }
    for(int i=0;i<y.length();i++){
        yy*=10;
        yy+=y[i]-'0';
    }
    return yy>=xx?0:INF;//yy>=xx 常数级别  
}
4.入读数据

(读入完后的工作以 work()省略)
map<Object , Object > map_name用法详细见 link[… ]

scanf("%d",&T);
    while(T--){//小明写了n个 A++ 程序 
        map<string ,int >map_times;//关联容器 将变量名与次数关联,可与下边胡map_on合并 
        map<string ,int >map_on;//储存变量的复杂度 
        stack<string >st;// top() 表示当前所在胡循环 
        isError=false;//错误开关关闭 
        Fnum=Maxon=on=0;//F-E记次清零 最大复杂度,当前复杂的清零 
        cin>>l>>On;//获取每个程序初始数据 行数与期望复杂度 
        string F,i,x,y;//准备 循环输入数据 
        while(l--){//程序行数 
            cin>>F;
            if(F=="F"){//首字母检测 F则开始循环 E则退出 
                cin>>i>>x>>y;
                work_1();
            }else{//首字母为 E
            	work_2();
            }
        }
        cout<<Ans<<endl;//输出答案 
    }
5.work()

好吧这道题恶心也就在ERR的时候
分析一下 ERR有这么几种情况~~(重复请忽视)~~

1. 程序行数L为奇数,则F,E肯定不匹配 (F,E个数不匹配)
2. F,E 出入栈不匹配
3. F,E 个数不匹配 (用Fnum记录,输入F则++,输入E则--4. F循环变量名字重复 (用map<string ,int >map_times 记录变量的次数)

注意!题目最后边还有一句话

attention.png

全部代码 (快捷复制:按住鼠标左键选择你要的代码然后Ctrl+c)
#include <bits/stdc++.h>
using namespace std;
int T,l,on,Fnum,Maxon;//T程序数,l程序行数,on当前复杂度,Fnum当前程序F-E个数,Maxon当前程序最大复杂度 
string On,Ans;//输入,答案 
bool isError;//当前程序错误开关 
const int INF=-99999;
const bool DEBUG=false; 

bool checkRight(int w){// 检查复杂度是否一置 
	if(w==0&&On=="O(1)") return 1; 
	else {
		int r=0;
		for(int i=4;i<On.length()-1;i++) {//这个for实际上就是从(string)On里取出数字来 
			r*=10;
			r+=On[i]-'0';
		}
		return r==w;
	}
}
int getOn(string x,string y){//获取复杂度 
	if(x!="n"&&y=="n") return 1;//从 常数 到 n :复杂度为 1 
	if(x=="n"&&y!="n") return INF;//从 n到常数 :由于无法进入故认为其复杂度为负无穷  依据题目 -99999 已经足够 
	if(x=="n"&&y=="n") return 0;//从 n到 n相当于 常数 到 常数 复杂度为 0 
	
	//x y 均为数字 ,则只要判断循环能否进入即可,能进入 复杂度为0 , 不能进入 复杂度则为 负无穷(INF) 
	int xx=0,yy=0;
	for(int i=0;i<x.length();i++){
		xx*=10;
		xx+=x[i]-'0';
	}
	for(int i=0;i<y.length();i++){
		yy*=10;
		yy+=y[i]-'0';
	}
	return yy>=xx?0:INF;//yy>=xx 常数级别  
}
int main(){
//	freopen("complexity.in","r",stdin);
//	freopen("complexity.out","w",stdout);
	scanf("%d",&T);
	while(T--){//小明写了n个 A++ 程序 
		map<string ,int >map_times;//关联容器 将变量名与次数关联,可与下边胡map_on合并 
		map<string ,int >map_on;//储存变量的复杂度 
		stack<string >st;// top() 表示当前所在胡循环 
		isError=false;//错误开关关闭 
		Fnum=Maxon=on=0;//F-E记次清零 最大复杂度,当前复杂的清零 
		cin>>l>>On;//获取每个程序初始数据 行数与期望复杂度 
		if(l%2) isError=true;//如果程序行数是个奇数,那么F-E一定无法一一对应 
		string F,i,x,y;//准备 循环输入数据 
		while(l--){
			cin>>F;
			if(F=="F"){//首字母检测 F则开始循环 E则退出 
				Fnum++;//F 计次 
				cin>>i>>x>>y;
				map_times[i]++;//当前程序中的 循环i的 次数 
				st.push(i);//将 i循环 压栈 ,栈顶 i即为当前所在循环  
				if(isError||map_times[i]>1){//如果已经error了,则没必要计算复杂度 
					isError=true;//如果循环i再次出现 >>ERR 
					continue;
				}
				map_on[i]=getOn(x,y);//获取 循环i 的复杂度 
				on+=map_on[i];//复杂度累加前边嵌套的 
				Maxon=max(on,Maxon);//最大值迭代 
			}else{//首字母为 E 则终止 st.top() 表示的循环 
				if(isError)continue;// error 跳过 
				Fnum--;//F 个数 减1 
				if(st.empty())continue;//栈为空 当前执行已不在任何循环内 
				on-=map_on[st.top()];//当前复杂度 减去 当前循环st.top() 的复杂度 
				map_times[st.top()]--;//循环i出现的次数 减1 
				st.pop();//当前循环 退栈 
			}
		}
		if(Fnum!=0||isError){//F-E个数不相等 , ERR输出 
			cout<<"ERR"<<endl;
			continue;
		}
		Ans=checkRight(Maxon)?"Yes":"No";//检查答案是否正确 
		cout<<Ans<<endl;//输出答案 
	}
	return 0;
}

最后更新 2018.10.30 19:01 By HaoDaDaDa

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值