[题解] NOIP2017 时间复杂度
题目描述
小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正确。
A++语言的循环结构如下:
F i x y
循环体
E
其中F \(i\) \(x\) \(y\) 表示新建变量 \(i\)(变量 \(i\) 不可与未被销毁的变量重名)并初始化为 \(x\) , 然后判断 \(i\) 和 \(y\) 的大小关系,若 \(i\) 小于等于 \(y\) 则进入循环,否则不进入。每次循环结束后 \(i\) 都会被修改成 \(i + 1\),一旦 \(i\) 大于 \(y\) 终止循环。
\(x\) 和 \(y\) 可以是正整数( \(x\) 和 \(y\) 的大小关系不定)或变量 \(n\) 。\(n\) 是一个表示数据规模的变量,在时间复杂度计算中需保留该变量而不能将其视为常数,该数远大于 100。
E 表示循环体结束。循环体结束时,这个循环体新建的变量也被销毁。
注:本题中为了书写方便,在描述复杂度时,使用大写英文字母 \(O\) 表示通常意义下 \(Θ\) 的概念。
输入格式
输入文件第一行一个正整数 \(t\) ,表示有 \(t( t \leq 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\), 其中 \(i\) 是一个小写字母(保证不为 \(n\) ),表示新建的变量名,\(x\) 和 \(y\) 可能是正整数或 \(n\) ,已知若为正整数则一定小于 \(100\) 。
程序行若以E开头,则表示循环体结束。
输出格式
输出文件共 \(t\) 行,对应输入的 \(t\) 个程序,每行输出 Yes 或 No 或者 ERR,若程序实际复杂度与输入给出的复杂度一致则输出 Yes,不一致则输出 No,若程序有语法错误(其中语法错误只有: ① F 和 E 不匹配 ②新建的变量与已经存在但未被销毁的变量重复两种情况),则输出 ERR。
注意:即使在程序不会执行的循环体中出现了语法错误也会编译错误,要输出 ERR。
题解
实际上一个A++程序的时间复杂度就是最高的带n循环嵌套层数。故在使用栈匹配循环头和循环结束语句的过程中模拟统计带n循环层数即可。
统计时需要注意两点,一是循环无法进入的情况,包括:
- \(x \geq y\)
- \(x = n, y\neq n\)
二是语法错误。语法错误一共有三种情况,包括:
- 出现了还未被释放的变量(使用vis数组记录)
- 在任意时刻E多过了F(判断栈空)
- 在最后时刻F多过了E(判断栈非空)
统计结束后,对比统计结果与题目给出的答案即可。
调试过程
对比初版代码,共发现以下错误:
- 没有考虑变量被回收的情况,没有重置vis标记
- \(x = y = n\)时可以进入循环但被我卡掉了,多加了不必要的判断
- 在还原tmp标记时没有考虑tmp修改时的条件(只有 \(able \neq 0\) 的时候才修改,所以只有able为0时才需要还原标记)
AC代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <string>
#include <stack>
#include <queue>
#define int long long
using namespace std;
bool vis[30]; // 用于标识字母是否出现过
struct LoopNode {
char id;
int x = 0, y = 0;
};
stack <LoopNode> st;
LoopNode readNode() {
LoopNode loopNode;
char ch = getchar();
while (ch == ' ') ch = getchar();
if (ch != 'n') {
while (isdigit(ch)) {
loopNode.x = (loopNode.x * 10) + ch - '0';
ch = getchar();
}
} else {
ch = getchar();
}
while (ch == ' ') ch = getchar();
if (ch != 'n') {
while (isdigit(ch)) {
loopNode.y = (loopNode.y * 10) + ch - '0';
ch = getchar();
}
} else {
ch = getchar();
}
return loopNode;
}
int readTar() {
int x = 0;
char ch = getchar();
while (ch != '(') ch = getchar();
ch = getchar();
if (ch == '1') {
while (ch != ')') ch = getchar();
return 0;
}
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
while (ch != ')') ch = getchar();
return x;
}
signed main() {
// freopen("test.in", "r", stdin);
int t;
cin >> t;
while (t--) {
memset(vis, 0, sizeof vis);
int n;
cin >> n;
int tar = readTar();
bool ok = true;
int able = 0; //记录当前有无不可进入的循环
int tmp = 0; //记录当前栈中的有效循环数
int ans = 0;
for (int i = 1; i <= n; i++) {
char op;
cin >> op;
if (op == 'F') {
char ch;
cin >> ch;
if (vis[ch - 'a']) ok = false;
else vis[ch - 'a'] = true;
LoopNode loopNode = readNode();
loopNode.id = ch;
st.push(loopNode);
if ((!loopNode.x && loopNode.y) || (loopNode.x && loopNode.y && loopNode.x > loopNode.y)) //无法进入:n,1 2,1
able++;
if (able) continue;
if (loopNode.x && !loopNode.y) tmp++;
} else if (op == 'E') {
if (st.empty()) {
ok = false;
} else {
ans = max(ans, tmp);
LoopNode loopNode = st.top();
if ((!loopNode.x && loopNode.y) || (loopNode.x && loopNode.y && loopNode.x > loopNode.y)) //无法进入:n,1 2,1
able--;
if (loopNode.x && !loopNode.y && able == 0) tmp--;
vis[loopNode.id - 'a'] = false;
st.pop();
}
}
}
if (!st.empty()) {
ok = false;
while (!st.empty()) st.pop();
}
if (ok) {
if (ans == tar) cout << "Yes\n";
else cout << "No\n";
} else {
cout << "ERR\n";
}
}
return 0;
}
标签:ch,int,题解,复杂度,while,时间,loopNode,getchar
From: https://www.cnblogs.com/Floyd3265/p/18129847