【洛谷】P3952 时间复杂度 题解

题目

小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正确。

A++语言的循环结构如下:

F i x y
循环体
E
其中F i x y表示新建变量 ii(变量 ii 不可与未被销毁的变量重名)并初始化为 xx, 然后判断 ii 和 yy 的大小关系,若 ii 小于等于 yy 则进入循环,否则不进入。每次循环结束后 ii 都会被修改成 i +1i+1,一旦 ii 大于 yy 终止循环。

xx 和 yy 可以是正整数(xx 和 yy 的大小关系不定)或变量 nn。nn 是一个表示数据规模的变量,在时间复杂度计算中需保留该变量而不能将其视为常数,该数远大于 100100。

“E”表示循环体结束。循环体结束时,这个循环体新建的变量也被销毁。

注:本题中为了书写方便,在描述复杂度时,使用大写英文字母“O”表示通常意义下“Θ”的概念。

分析

我们要做的事情是对一组for循环进行时间复杂度的计算,
这些for循环是可以嵌套的,那么for循环的嵌套不就可以看成是一个多叉树嘛。

那么求时间复杂度 => 求多叉树的最大深度

那么一个树的遍历就搞定了。

但是题目输入的是字符串,我们想要的树,所以先根据题目输入的字符串构造一颗树,与此同时进行语法判断。

构造树那么就需要一个来辅助,大致思路就是遍历字符串数组,根据是F 还是 E 来压栈,弹栈,构造树。

与此同时,为了保证可以识别,新建的变量与已经存在但未被销毁的变量重复 我们需要在每个树的节点维护一个集合,来保留当前作用域下出现的变量,同时有一个指向父节点的指针,来递归的判断当前这个变量是否在当前节点之前出现过。

大致思路就是这个样子

代码

#include <iostream>
#include <vector>
#include <string>
#include <stack>
#include <set>

using namespace std;

struct line {
    string F, i, x, y;
    vector<line*> next; // 所有的子节点
    line * fa; // 指向父节点的指针
    set<string> sympols; // 当前出现的变量

    line() : fa(this) {}
    line(line * fa) : fa(fa) {}

	// 判断一个变量是否出现过
    bool exist(const string& str) {
    	// 如果当前节点是根节点,直接返回不存在
        if (this->fa == this) return false;

		// 如果在当前的变量集合里
        if (sympols.find(str) != sympols.end()) 
            return true;
            
        // 不在就递归的判断
        return fa->exist(str);
    }

	// 进行插入一个子节点
    bool insert(line* l) {
        if (exist(l->i)) return false;
        l->fa = this;
        l->sympols.insert(l->i);
        next.push_back(l);
        return true;
    }

	// 调试用的
    friend ostream & operator<< (ostream&out, const line& l){
       return  out << l.F << " " << l.i << " " 
           << l.x << " " << l.y ;
    }
};

// 输出树的结构,调试用的。
void output(line* l, int cnt) {
    for (int i = 0; i < cnt; i++) {
        cout << "-";
    }
    cout << *l << endl;
    for (line* ll : l->next) {
        output(ll, cnt + 1);
    }
}

// 找到树的最大深度
int dfs(line* root) {
    int sum = 0;
    for (line* l : root->next) {
        int tmp = 0;
        if (l->x == "n" && l->y == "n") tmp = 0;
        else if (l->x == "n" && l->y != "n") return 0;
        else if (l->x != "n" && l->y != "n") {
            if (atoi(l->x.c_str()) > atoi(l->y.c_str()))
                return 0;
            else
                tmp = 0;
        } else {
            tmp = 1;
        }
        sum = max(sum, tmp + dfs(l));
    }
    return sum;
}

// 将字符串转换成树型结构,并返回时间复杂度
string judge(line&root, vector<line>&program) {
    // 进行编译
    int deep = 0;
    stack<line*> op;
    op.push(&root);
    for (size_t i= 0; i < program.size(); i++) {
        line*tmp = &program[i];
        if (tmp->F == "E") {
            if (op.size() == 1) {
                deep = -1;
                break;
            }
            op.pop();
        } else {
            if (op.top()->insert(tmp)) {
                op.push(tmp);
            } else {
                deep = -1;
                break;
            }
        }
    }

    if (deep == -1 || op.size() != 1) return "ERR";

    // output(&root, 0);

    deep = dfs(&root);
    if (deep == 0) return "O(1)";
    else return "O(n^" + to_string(deep) + ")";
}

int main() {
    int t, n;
    cin >> t;
    string result;
    for (int i = 0; i < t; i++) {
        line root;
        root.F = "root";
        cin >> n >> result;
        vector<line> program(n);
        for (int i = 0; i < n; i++) {
            cin >> program[i].F;
            if (program[i].F == "E") continue;
            cin >> program[i].i >> program[i].x >> program[i].y;
        }
        string res = judge(root, program);
        // cout << res << endl;
        if (res == "ERR") cout << res << endl;
        else if (res == result) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值