NOIP 2017 D1T2 时间复杂度
题目链接
先发点牢骚
不得不说,这真是道傻逼题,再noip考场上我调了2.5h也没调出来,结果17年的noip就这样炸了。。。
之后再做这道题,就是18年的事了,虽然隔了很长时间,但题目仍然很熟悉,拿到手之后就开始做。结果发现这题目依旧恶心,做了一下午+一晚上,才把这道题调出来。
这道题的解法无非就是栈或者递归,层主拿到题之后首先想到的是遇到F之后就把循环压进站,遇到E再弹出,结果就有了考场上2.5h xjb调试无果。。。可能栈做法的代码比较短,复杂度也比递归要低,但像我这样的蒟蒻实在是驾驭不了,最后写了递归。
言归正传
对于输入的处理
多组数据
首先,这道题是多组数据,不过这不是问题,循环n遍,每次注意初始化就可以了,这应该不是什么大问题。
输入与存储
因为这道题有ERR这种恶心情况的存在,导致在线做时处理起来很难受,所以先把输入的数据存储起来,然后用离线做法,这样就简化了许多代码。
存储的时候,题目的输入格式是有很多行,每一行中都有许多空格,这样做起来就十分麻烦,而且对于linux与windows换行符的区别我至今都搞不清楚,所以我每输入一行就把其中的字母和数字提取出来,把二维的字符数组压缩到一维的字符串,做起来就轻松不少了。
数字的处理
由于循环开始的条件和结束的条件可能都是数字,所以再压缩时两个数字就会连再一起,无法判断大小。所以需要压缩时将每个数字的最后加上一个‘.’。
在代码中笔者用now_来表示当前处理到的位置。一定要处理好now_的值,一次不对可以反复调试,耐心一点,一处处排除错误。
递归的过程
zero函数
在处理每组数据时,笔者为了方便,设置了一个第零层,即代码中的zero函数,zero函数的返回值共有三种情况
返回-2
即程序出错,不用考虑后面的循环,直接跳出输出ERR。
-2的得出有以下几种途径
1.在第零层时遇到E,相当于栈的下溢
2.所有程序都运行完后仍未返回第零层
3.循环过程中的新建变量与已经使用过的变量重复
第一种错误是在zero函数中进行判断 二三种则在之后的递归函数中判断。
返回0
返回零意味程序的复杂度为常数级别,即O(1)
返回值大于零
返回的数值就是底数为n的乘方中的指数
在给定的循环都结束后,在所有复杂度中取最大值返回main函数,与小明猜测的复杂度比较。
digui函数
没错,函数的名字就是这样简单粗暴(层主英语没学好),digui函数就是循环的主体,是整篇代码的核心。
在zero函数中读取到F时,就会进入digui函数,首先,从压缩后的字符串中找到循环的新建变量,并判断是否与之前循环的变量重复。
getmath函数
再次原谅层主英语没学好
当遇到需要读取一个数字时,就使用getmath函数,读取完之后需要继续向后移一位,来跳过用来分隔数字的‘.’。
循环复杂度的判定
1.循环出错,需要返回-2,忽略其他循环,直接输出答案ERR。
2.该循环不执行,不论与之嵌套的循环复杂度是多少,均返回常数复杂度,这里用0代表常数复杂度。
循环不执行的情况分为两种,一是起始条件为n,但终止条件是一个数字,二是起始与终止条件都是数字,但起始条件大于终止条件。
有些读者会问,既然内层的循环不执行,为什么还要递归下去?
因为对于程序出错的判定,是不论程序是否运行,都会算作错误,所以为了检查其内部是否有错误,需要继续递归下去,但不论如何都应返回常数复杂度。
3.循环的复杂度是常数
出现常数的情况在起始条件与终止条件都是数字而且起始条件小于等于终止条件时或起始条件与终止条件都为n时会出现。
出现常数复杂度后的返回值是在其内部的循环中取次数最高的返回,如果内部没有循环或内部循环都为常数复杂度时,仍返回常数复杂度。
4.该层循环的复杂度是n的一次方
这种情况是在只有在起始条件为数字,终止条件时n时才会出现。 返回值与情况三类似,只不过要在次数上加一。
最后,当循环正常运行完毕返回到zero函数时,取n的最高次项,返回到main函数中,与小明猜测的复杂度比较,或程序出现错误,中途强行终止,返回-2,即可得出答案。
一些(无关紧要的)小细节
变量名
每个OIer都有自己取变量名的习惯,所以有的时候看其他人的程序会很难受,层主在写这道题的时候为了理解方便绝大多数变量都使用的拼音或简单的英文单词组合,尽量让大家读起来方便。
f4~f11 是程序中的一些中间变量,基本用于提取输入中所提供的数字。
其实f1~f3本来是有的,但在调试过程中去掉了。
use表示循环变量是否使用过。
s、s1、ss则是用来存储输入和压缩后的字符串。
len表示压缩后的字符串长度。
hangshu就是行数,fuzadu就是复杂度。
haha存储最终的答案。
程序的一些缺点
毕竟我是个初三的蒟蒻,不论是码力还是算法,都比不上luogu里的dalao们,程序写的很冗杂,因为是一点一点调试出来的,本来不长的代码最后加到了两百行,我也不习惯把代码压缩的很紧,可读性还是十分重要的,我不希望因为很差的可读性而使一些刚入门的人望而却步(dalao们请无视这句话)。
总结
这是层主第一次写博客,有点紧张,可能语言不是很准确生动,还望看官们多多包涵。
下面是层主的QQ,如果有什么不理解的地方或者建议可以在博客中评论,也可以加我QQ私聊。
QQ:2845600375
如有谬误 敬请斧正
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,ques,f4,f5,len,now_,cym,hangshu,haha[11],f6,f7,f8,f9,f10,f11; bool use[30]; char s[1001],ss[1001],s1[1001]; int getmath() { int kkk=0; while((ss[now_]>='0')&&(ss[now_]<='9')) { kkk*=10; kkk+=ss[now_]-'0'; now_++; } now_++; return kkk; } int digui() { int ans=-3,fuzadu,flag; now_++; if(use[ss[now_]-'a']==true) { return -2; } use[ss[now_]-'a']=true; flag=ss[now_]-'a'; now_++; if((ss[now_]>='0')&&(ss[now_]<='9')) { f4=getmath(); if(ss[now_]!='n') { f11=getmath(); if(f4>f11) { fuzadu=-1; } else { fuzadu=0; } } else { fuzadu=1; now_++; } } else { now_++; if(ss[now_]=='n') { now_++; fuzadu=0; } else { getmath(); fuzadu=-1; } } ans=-3; while(now_<=len) { if(ss[now_]=='F') { f5=digui(); if(f5==-2) { use[flag]=false; return -2; } if(f5==-1) { if((ans==-3)) ans=-1; continue; } if(f5==0) { if((ans==-3)||(ans==-1)) ans=0; continue; } ans=max(ans,f5); } if(ss[now_]=='E') { now_++; use[flag]=false; if(ans>=0) if(fuzadu==1) ans++; if(ans==-3) { ans=fuzadu; } if(fuzadu==-1) ans=0; use[flag]=false; return ans; } } use[flag]=false; return -2; } int zero() { f7=-3; while(now_<=len) { if(ss[now_]=='F') { f6=digui(); if(f6==-2) return -2; else f7=max(f7,f6); continue; } if(ss[now_]=='E') { return -2; } } return f7; } int main() { scanf("%d",&n); cin.getline(s1,100); for(int w=1;w<=n;w++) { cin.getline(s1,100); f8=0;f9=0; while((s1[f8]>='0')&&(s1[f8]<='9')) { f9*=10; f9+=s1[f8]-'0'; f8++; } hangshu=f9; if(s1[f8+3]=='1') ques=0; else { f8+=5;ques=0; while(s1[f8]!=')') { ques*=10; ques+=s1[f8]-'0'; f8++; } } len=0; for(int i=1;i<=hangshu;i++) { cin.getline(s,101); for(int j=0;j<=strlen(s);j++) { if((!((s[j]>='0')&&(s[j]<='9')))&&((s[j-1]>='0')&&(s[j-1]<='9'))) { len++; ss[len]='.'; } if( (s[j]=='E')||(s[j]=='F') || ( ( s[j]>='a' )&&( s[j] <= 'z' ) )||((s[j]>='0')&&(s[j]<='9'))) { len++; ss[len]=s[j]; } } } now_=1; cym=zero(); if(cym==-2) { haha[w]=-1; continue; } if(ques==cym) haha[w]=1; else haha[w]=0; } for(int i=1;i<=n;i++) { if(haha[i]==-1) printf("ERR"); if(haha[i]==0) printf("No"); if(haha[i]==1) printf("Yes"); printf("\n"); } return 0; }