【提高组NOIP2017】时间复杂度 题解 分治系统处理

原题边幅很长,这里就不贴出来了,落谷有原题,不清楚的可以去看看

------------------------------------------------------------------------------------------------------------

这是一道大模拟,我的方法是建立输入、编译和判断三大系统。

 

这三大系统用三个函数实现,分别是

void Input() //输入系统,专门读取数据并把字符串内的有价值数据提取出来

bool complite() //编译系统,对整组数据进行语法检查,返回true代表语法正确

bool work() //判断系统,符合时间复杂度返回true,否则返回false

 -------------------------------------------------------------------------------------------------------------------

主要流程:

 

所以主函数是这样的:

int main()
{
	cin>>t;
	for(int i=0; i<t ;i++){ //循环t次,每组输入一组数据
	Init(); //该函数的作用为初始化所有数组与全局变量
	Input(); //读入数据
/*编译成功就对数据进行判断并输出”Yes”或”No”,否则输出”ERR”*/
	if(complite()){
		if(work()) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	else cout<<"ERR"<<endl;
	}
	return 0;
}





 ----------------------------------------------------------------------------------------------------------------------------

那么接下来的任务就很明确了,就是一个个实现三大系统

 

我们读到的数据是字符串,字符串是不方便直接处理的,所以需要输入系统的数据提取功能,把字符串的有用的同类型数据转化成一个个数组存取,具体怎么提取,还要看接下来编译与判断系统需要的是字符串中的哪些信息。在本题目中,我把一行字符串看成由以下四类数据组成。

 

字符串开头非FEO(n^w)这行不需要进行编译和判断,只需要把w提取出来就行了。

stringF型(即开头为FE型以此类推)时才会有i x y

 

了解了字符串的数据组成后,那么数据的提取也就变得很简单了:

开出这下面四个数组

1. 用于记录第i行的字符串类型的数组type[]

2. 用于记录第i行的计数变量名的数组ch[]

3. 用于记录第i行的计数变量的值的数组x[]

4. 用于记录第i行的循环边界值的数组y[]

 -------------------------------------------------------------------------------------------------------------------------

编译系统用到了其中的type[]ch[]数组,看看其工作原理吧:

 

对应代码:

bool complite()
{
/*先检查变量名,看有没有毛病*/
	for(int i=1; i<n ;i++){
		if(type[i] == _F){
			if(var[ch[i]]) return false; //重复定义了变量
			else{
				var[ch[i]] = true;//建立变量
				Steak[++top] = ch[i]-'a';//储存该层循环的变量
			}
		}
		else if(type[i] == _E){
			if(top >= 0)
				var[Steak[top--]] = false;//退出循环,销毁该层循环变量
		}
	}
/*检查F与E是否都匹配上*/
	int steak = 0;//小F栈
	for(int i=1; i<n ;i++){
		if(type[i] == _F) steak++;
		if(type[i] == _E){
			if(steak <= 0) return false; //E多了出来
			steak--;
		}
	}
	if(steak > 0) return false; //有尚未匹配的F
	return true;//无语法错误,返回真
}



------------------------------------------------------------------------------------------------------------------------------------

而判断系统用到了type[]x[],  y[],由于流程较长,就用伪代码粗略讲下,稍后会给全部程序

能进入判断系统,说明所有语句都是合法的,每一个F都会有与其匹配的E

对于计算循环体的时间复杂度,我们可以采用递归的方式

Int pos2; //全局变量,代表当前正在处理的行数
Int Jisuan(int pos) //pos表示计算的是第pos行循环的的时间复杂度
{
	pos2 = pos+1;
	If(第pos2行的字符串类型为E型){
		pos2++;
		根据第pos行的x与y的关系返回0或1;
	}
 
	If(第pos行循环并没有进入){
		跳过第pos行循环包含的所有循环并返回0;
	}
	Else{
		由第pos行的x和y关系算出该层循环本身固有的时间复杂度为1或n,并储存在now中
		循环(只要第pos2行不是E型字符串){
			Max = max(jisuan(各个子循环))
		}
	}
 
	now += Max;
	return now;
}




计算出循环体的时间复杂度后,可用来与题目标出来的时间复杂度向比较看是否匹配

Bool work()
{
	int Max; //最大时间复杂度循环体的时间复杂度
	循环(i 1:n){
		If(第i行字符串为F类型){
			Max = max(Max, jisuan(i));
			i = pos2;
		}
	}
 
	If(Max == 规定时间复杂度) return true;
	Else return false;
}
 




这就是判断系统的原理了。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

那么剩下来的就是数据的提取了,也就是输入系统的部分,我采用了getline()的方式去读取,事实上从字符串中提取数据的方法多种多样,我的并不一定是最好的,这个大家可以到网上去查大神的操作,不过这里我还是得贴出全部代码,不然以上讲的程序也不成整体。

-------------------------------------------------------------------------------------------------------------------

全代码:

#include <iostream>
#include <string>
#include <cstring>
#define MAX 10000
#define N 9999999
using namespace std;

enum Type{
	_F,
	_E
};
string data[MAX];//输入的数据 
/*设第i行的语句为 "F i x y" 则以下全局变量的作用*/
int type[MAX]; //若type[i] = _F ,说明该行是以F开头的语句 
int ch[MAX]; //用于记录第i行的计数变量名的数组
int x[MAX]; //用于记录第i行的计数变量的值的数组
int y[MAX]; //用于记录第i行的循环边界值的数组
/************************************************/
bool var[30];//变量表,0~25对应a~z,true表示变量已建立 
int Steak[MAX];//F栈,用于编译系统,方便销毁变量 
int top = -1; //栈顶指针  

int time; //记录输入数据中要求的时间复杂度 

int pos2; //正在处理的行数,用于判断系统 

int t;//共有t组数据 
int n;//该组数据有n行(实际上包括O(n^w)这行有n+1行) 

void Init()
{
	memset(var, 0, sizeof(var));
	memset(type, 0, sizeof(type));
	memset(x, 0, sizeof(x));
	memset(y, 0, sizeof(y));
	memset(Steak, 0, sizeof(Steak));
	memset(ch, 0, sizeof(ch));
	time = 0;
	pos2 = 0;
} 

void Input()
{
	int temp = 0;
	int pos;
	cin>>n;
	n++;
	for(int i=0; i<n ;i++)
	{
		getline(cin, data[i]);
		
		if(data[i][1] == 'O'){
			for(int j=4; j<data[i].size() ;j++)
				if(data[i][j]<='9' && data[i][j] >= '0')
					temp = temp*10 + data[i][j]-'0';
					
			time = temp;
		}
		else if(data[i][0] == 'F'){
			type[i] = _F;
			
			ch[i] = data[i][2];
			
			pos = 4;
			temp = 0;
			while(data[i][pos] != ' '){
				if(data[i][pos] == 'n'){
					x[i] = N;
				}
				else{
					temp = temp*10 + data[i][pos]-'0';
				}
				
				pos++;
			}
			
			if(x[i] != N) x[i] = temp;
			
			temp = 0;
			
			pos++;
			
			while(pos < data[i].size()){
				if(data[i][pos] == 'n'){
					y[i] = N;
				}
				else{
					temp = temp*10 + data[i][pos]-'0';
				}
				
				pos++;
			}
			
			if(y[i] != N) y[i] = temp;
		}
		else if(data[i][0] == 'E'){
			type[i] = _E;
		}
	}	
}

bool complite()
{
	/*先检查变量名,看有没有毛病*/
	for(int i=1; i<n ;i++){
		if(type[i] == _F){
			if(var[ch[i]]) return false; //重复定义了变量 
			else{
				var[ch[i]] = true;//建立变量 
				Steak[++top] = ch[i]-'a';//储存该层循环的变量 
			}
		}
		else if(type[i] == _E){
			if(top >= 0)
				var[Steak[top--]] = false;//退出循环,销毁该层循环变量 
		}
			
	}
	
	/*检查F与E是否都匹配上*/
	int steak = 0;//小F栈 
	for(int i=1; i<n ;i++){
		if(type[i] == _F) steak++;
		if(type[i] == _E){
			if(steak <= 0) return false; //E多了出来 
			
			steak--;
		}
	}
	if(steak > 0) return false; //有尚未匹配的F 
	
	return true;//无语法错误,返回真 
}

int jisuan(int pos)
{
	if(type[++pos2] == _E){
		if(x[pos] != N && y[pos] == N){
			pos2++;
			return 1;	
		}
		else{
			pos2++;
			return 0;
		}
	}
	int now = 0;
	int steak = 1;
	if(x[pos] > y[pos]){ //没进入循环 ,跳过所有循环并返回0 
		while(steak != 0){
			if(type[pos2++] == _E) steak--;
			else steak++;
		}
		
		return 0;
	}
	
	int Max = 0;
	int temp = 0;
	if(x[pos] <= y[pos]){ //进入循环
 		if(x[pos] != N && y[pos] == N) now++;
 		
		while(type[pos2] != _E){
			temp = jisuan(pos2);
			if(temp > Max) Max = temp;
		}
	}
	
	pos2++;
	now += temp;
	return now;
}

bool work()
{
	int Max = 0;

	for(int i=1; i<n ; i=pos2){
		if(type[i] == _F){
			pos2 = i;
			Max = max(Max, jisuan(i));
		}
	}

	if(Max == time) return true;
	else return false;
}

int main()
{
	freopen("text.in", "r", stdin);
	cin>>t;
	for(int i=0; i<t ;i++){
		Init();
		Input();
		if(complite())
			if(work()) cout<<"Yes"<<endl;
			else cout<<"No"<<endl;
		else cout<<"ERR"<<endl;
	}
	
	return 0;
}


 



                                    
展开阅读全文

Git 实用技巧

11-24
这几年越来越多的开发团队使用了Git,掌握Git的使用已经越来越重要,已经是一个开发者必备的一项技能;但很多人在刚开始学习Git的时候会遇到很多疑问,比如之前使用过SVN的开发者想不通Git提交代码为什么需要先commit然后再去push,而不是一条命令一次性搞定; 更多的开发者对Git已经入门,不过在遇到一些代码冲突、需要恢复Git代码时候就不知所措,这个时候哪些对 Git掌握得比较好的少数人,就像团队中的神一样,在队友遇到 Git 相关的问题的时候用各种流利的操作来帮助队友于水火。 我去年刚加入新团队,发现一些同事对Git的常规操作没太大问题,但对Git的理解还是比较生疏,比如说分支和分支之间的关联关系、合并代码时候的冲突解决、提交代码前未拉取新代码导致冲突问题的处理等,我在协助处理这些问题的时候也记录各种问题的解决办法,希望整理后通过教程帮助到更多对Git操作进阶的开发者。 本期教程学习方法分为“掌握基础——稳步进阶——熟悉协作”三个层次。从掌握基础的 Git的推送和拉取开始,以案例进行演示,分析每一个步骤的操作方式和原理,从理解Git 工具的操作到学会代码存储结构、演示不同场景下Git遇到问题的不同处理方案。循序渐进让同学们掌握Git工具在团队协作中的整体协作流程。 在教程中会通过大量案例进行分析,案例会模拟在工作中遇到的问题,从最基础的代码提交和拉取、代码冲突解决、代码仓库的数据维护、Git服务端搭建等。为了让同学们容易理解,对Git简单易懂,文章中详细记录了详细的操作步骤,提供大量演示截图和解析。在教程的最后部分,会从提升团队整体效率的角度对Git工具进行讲解,包括规范操作、Gitlab的搭建、钩子事件的应用等。 为了让同学们可以利用碎片化时间来灵活学习,在教程文章中大程度降低了上下文的依赖,让大家可以在工作之余进行学习与实战,并同时掌握里面涉及的Git不常见操作的相关知识,理解Git工具在工作遇到的问题解决思路和方法,相信一定会对大家的前端技能进阶大有帮助。

实用主义学Python(小白也容易上手的Python实用案例)

12-24
原价169,限时立减100元! 系统掌握Python核心语法16点,轻松应对工作中80%以上的Python使用场景! 69元=72讲+源码+社群答疑+讲师社群分享会  【哪些人适合学习这门课程?】 1)大学生,平时只学习了Python理论,并未接触Python实战问题; 2)对Python实用技能掌握薄弱的人,自动化、爬虫、数据分析能让你快速提高工作效率; 3)想学习新技术,如:人工智能、机器学习、深度学习等,这门课程是你的必修课程; 4)想修炼更好的编程内功,优秀的工程师肯定不能只会一门语言,Python语言功能强大、使用高效、简单易学。 【超实用技能】 从零开始 自动生成工作周报 职场升级 豆瓣电影数据爬取 实用案例 奥运冠军数据分析 自动化办公:通过Python自动化分析Excel数据并自动操作Word文档,最终获得一份基于Excel表格的数据分析报告。 豆瓣电影爬虫:通过Python自动爬取豆瓣电影信息并将电影图片保存到本地。 奥运会数据分析实战 简介:通过Python分析120年间奥运会的数据,从不同角度入手分析,从而得出一些有趣的结论。 【超人气老师】 二两 中国人工智能协会高级会员 生成对抗神经网络研究者 《深入浅出生成对抗网络:原理剖析与TensorFlow实现》一书作者 阿里云大学云学院导师 前大型游戏公司后端工程师 【超丰富实用案例】 0)图片背景去除案例 1)自动生成工作周报案例 2)豆瓣电影数据爬取案例 3)奥运会数据分析案例 4)自动处理邮件案例 5)github信息爬取/更新提醒案例 6)B站百大UP信息爬取与分析案例 7)构建自己的论文网站案例
©️2020 CSDN 皮肤主题: 游动-白 设计师: 上身试试 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值