洛谷 P3952 时间复杂度

做这道题的最大收获就是坚持不懈,勇往直前,这道题是一个“码力题”,不能说无脑,但绝对恶心。总共花了3h+才调出来。果然当时NOIp放弃这道题是明智的

好了,闲话放一边,我们来搞一搞这道题。

这道题思路很简单,就是模拟。t<=10和L<=100也提示了我们时间不是问题,要大胆的去模拟。我是在线判断程序是否ERR,离线判断时间复杂度计算是否正确。如果程序ERR,就标记一下,输入完之后直接结束这次循环(continue),不再进行接下来的计算。至于判断Yes和No,我先将输入的时间复杂度记录下来,自己再算一遍时间复杂度,和所给的比对,来判断Yes和No(做法很乱,请读者老爷们包容)。

这是一个基本思路,中间还有许多特判,包括:

  1. 两个ERR的判断:第一个用的栈的思想,第二个用的桶的思想
  2. 因为我是输入的字符串,所以还要将字符串中的数字扣下来,便于使用和比对
  3. 判断该层循环能否进去
  4. 有可能出现几个循环并列在同一层的情况
  5. 寻找O(n)最大循环层数,就是时间复杂度,O(1)的情况直接不考虑

这就是我的一个大致思路,具体实现可以看我的代码

#include<bits/stdc++.h>
using namespace std;
int t;
string ans[101];
bool flag[127];//虽然这是一个数组,但却只用到了flag[22]
               //flag[22]:判断该层循环能否进入。
int flag2,tong[27];//flag2用来判断ERR 1;tong用来判断ERR 2
int yyy,tot,a,maxx;//yyy记录当前所在循环的时间复杂度
                   //tot记录当前所在的循环
                   //a记录输入的时间复杂度 n^w中的w
                   //maxx记录正确的w
bool boom[100];    //boom[i]记录第i层循环是否到过
int main()
{
    cin>>t;
    //======输入,同时判断是否ERR
    for(int i=1;i<=t;i++)
    {
        tot=0,flag2=0,memset(tong,0,sizeof(tong)),a=0;
        int l; cin>>l;
        string o; cin>>o;
        if(o[2]==‘n‘)  //因为n的位置固定,所以直接判断该位置是否为n
                        //下面的求a和抠数也是同理
        {
            a=o[4]-‘0‘;
            if(o[5]>=‘0‘&&o[5]<=‘9‘)
              a=a*10+o[5]-‘0‘;
        }
        char codes[101][101];
        getchar();
        for(int j=1;j<=l;j++)
          for(int k=0;k<=10;k++)
          {
            codes[j][k]=‘ ‘;
          }
        for(int j=1;j<=l;j++)
        {

            gets(codes[j]);
            //=====ERR 1
            if(codes[j][0]==‘F‘) flag2++; //栈的思想,类似于括号匹配
            if(codes[j][0]==‘E‘) flag2--;
            if(flag2<0) ans[i]="ERR";

            //=====ERR 2
            if(codes[j][0]==‘F‘)
            {
                if(tong[codes[j][2]-‘a‘+1]==0)  //桶的思想
                  tong[codes[j][2]-‘a‘+1]=++tot; //因为循环结束后变量销毁,所以要记录循环层数
                else
                  ans[i]="ERR";
            }
            if(codes[j][0]==‘E‘)
            {
                for(int i=1;i<=26;i++)
                {
                    if(tong[i]==tot)  // 销毁变量
                      tong[i]=0;
                }
                tot--;
            }
        }
        if(flag2>0) ans[i]="ERR";

        if(ans[i]=="ERR")  continue; //如果ERR,不执行下面的操作
        //================ERR finish

        tot=0;int zzz=0;flag[22]=0;yyy=0;memset(boom,0,sizeof(boom));maxx=0;  //一定要初始化
        for(int j=1;j<=l;j++)
        {
            if(codes[j][0]==‘F‘)
            {
                tot++;
                if(codes[j][4]==‘n‘&&((codes[j][6]>‘0‘&&codes[j][6]<=‘9‘)||(codes[j][7]>‘0‘&&codes[j][7]<=‘9‘))&&!flag[22])
                {
                    flag[22]=1;  //因为n>任何一个整数,所以该层循环无法进入
                    zzz=tot;     //zzz记录无法进入的循环层数
                }
                if(codes[j][4]>=‘0‘&&codes[j][4]<=‘9‘&&!flag[22]&&(codes[j][6]!=‘n‘&&codes[j][7]!=‘n‘))
                {
                    // 如果两个都是常数,那么暴力抠数,比较两个数的大小
                    int c1=codes[j][4]-‘0‘;
                    int c2;
                    if(codes[j][5]>=‘0‘&&codes[j][5]<=‘9‘)
                      c1=c1*10+codes[j][5]-‘0‘;

                    if(c1<10)
                      if(codes[j][6]>=‘0‘&&codes[j][6]<=‘9‘)
                      {
                          c2=codes[j][6]-‘0‘;
                          if(codes[j][7]>=‘0‘&&codes[j][7]<=‘9‘)
                            c2=c2*10+codes[j][7]-‘0‘;
                      }
                    if(c1>10)
                      if(codes[j][7]>=‘0‘&&codes[j][7]<=‘9‘)
                      {
                          c2=codes[j][7]-‘0‘;
                          if(codes[j][8]>=‘0‘&&codes[j][8]<=‘9‘)
                            c2=c2*10+codes[j][8]-‘0‘;
                      }
                    if(c1>c2)  //前面的数大于后面的数
                    {
                        flag[22]=1;
                        zzz=tot;
                    }
                }
                if((codes[j][6]==‘n‘||codes[j][7]==‘n‘)&&codes[j][4]!=‘n‘&&!flag[22])  //该层循环可以进去
                {
                    if(!boom[tot])  //且没有进去过同层的循环,因为如果进去过同层的循环,那么这次循环不会影响时间复杂度
                    {
                        boom[tot]=1; //标记
                        yyy++;  //记录当前的时间复杂度
                        if(yyy>maxx)  //如果yyy此时大于maxx,更新maxx
                          maxx=yyy;
                    }
                }

            }
            if(codes[j][0]==‘E‘)
            {
                if(tot==zzz)
                  flag[22]=0;  //如果之前该层循环无法进入,那么到同层的E,该层循环结束,以后的循环就可以进去了
                if(boom[tot])
                {
                    yyy--; //如果到过这层循环,退出这层循环
                    boom[tot]=0; //再次标记该层循环未到过,防止特判4出现
                }
                tot--;  //层数减一
            }
        }
        if(maxx==a)  //比较输入的时间复杂度和我们算出的正解
          ans[i]="Yes";
        else
          ans[i]="No";
    }
    for(int i=1;i<=t;i++)
      cout<<ans[i]<<endl;

    return 0;
} // 140行,完结撒花 

原文地址:https://www.cnblogs.com/wxl-Ezio/p/8491306.html

时间: 2024-10-03 21:15:28

洛谷 P3952 时间复杂度的相关文章

洛谷P3952 时间复杂度

题目:https://www.luogu.org/problemnew/show/3952 题目描述 小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序来判断小明对他的每个程序给出的时间复杂度是否正确. A++语言的循环结构如下: F i x y 循环体 E 其中F i x y表示新建变量 i(变量 i 不可与未被销毁的变量重名)并初始化为 x, 然后判断 

[题解]洛谷比赛『期末考后的休闲比赛2』

[前言] 这场比赛已经结束了有几天,但我各种忙,虽然AK但还是没来得及写题解.(我才不会告诉你我跑去学数据结构了) T1 区间方差 (就不贴题好了) 首先可以推公式(我们可以知道,线段树然而并不能通过初中学过的方差公式在log(L)内求出方差): (s2表示方差,L表示区间长度,xi表示区间的每一项,最后一个x上画了一根线表示这些数据的平均数) 用二项式定理完全平方公式可得: 再次展开: 另外,再代入以下这个 得到了: 然后继续吧.. 然后duang地一声合并同类项,于是我们得到了: 然后可以高

洛谷P1083 借教室 二分 + 差分

洛谷P1083 借教室 二分 + 差分(或说前缀和,其实前缀和更准确一点) 首先二分答案,即取 mid 个人,且他们不会冲突 然后O(n) 判断是否冲突 如何判断呢,首先我们发现 一个人的操作相当于是将 一些连续的山削去了一个高度 然后我们可以记录这座山被消了多少高度,但这样一次就要 O(N) 总共(n^2) 但是我们发现高度差只有两个地方变了,一个是起始,一个是终止 t[ i ] 表示 h[ i ] - h[ i-1 ] 改变过后 于是 t[ s ]-=d,t[ t+1 ]+=d ; 然后这样

【洛谷】【洛谷月赛】4月月赛Round 1/2

洛谷月赛"月"来"月"丧了,一月更比一月丧,做得我十分不"月"-- 4月的两轮月赛,都只会T1,就写一下吧,等待后续更新-- 先看看Round1的T1: [R1T1] 网址:点我 [题意简述] 给定一个长度为n的序列,其中的元素均是1~m之间的正整数. 要求从中选出k个数,交换它们的位置,其他未被选中的数保持不变,使得变换后的序列中,相等的数总是排在一段连续区间. 要求最小化k. 1<=n<=105,1<=m<=20 [思

洛谷P2766-最长递增子序列问题

chunlvxiong的博客 题目描述: 给定正整数序列x1,...,xn (1≤n≤500). 1.计算其最长递增子序列的长度s. 2.计算从给定的序列中最多可取出多少个长度为s的递增子序列. 3.如果允许在取出的序列中多次使用x1和xn,则从给定序列中最多可取出多少个长度为s的递增子序列. 思考&分析:  第一问应该比较easy,利用DP求解,时间复杂度O(N^2)--利用线段树可以优化到O(NlogN),但是没这个必要. 第二问:考虑使用网络流求解. 首先把所有点x拆成两个点xa,xb,每

[codevs1048]石子归并&amp;&amp;[codevs2102][洛谷P1880]石子归并加强版

codevs1048: 题目大意:有n堆石子排成一列,每次可合并相邻两堆,代价为两堆的重量之和,求把他们合并成一堆的最小代价. 解题思路:经典区间dp.设$f[i][j]$表示合并i~j的石子需要的最小代价.则有$f[i][j]=min(f[i][k]+f[k+1][j]+\sum\limits _{l=i}^{j}a[l])$,时间复杂度$O(n^3)$. C++ Code: #include<cstdio> #include<cstring> using namespace s

洛谷OJ P1141 01迷宫 解题报告

洛谷OJ P1141 01迷宫 解题报告 by MedalPluS [题目描述]    有一个仅由数字0与1组成的n×n格迷宫.若你位于一格0上,那么你可以移动到相邻4格中的某一格1上,同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上.你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身).   [输入描述]   输入的第1行为两个正整数n,m.  下面n行,每行n个字符,字符只可能是0或者1,字符之间没有空格.  接下来m行,每行2个用空格分隔的正整数i,j,对

洛谷P1462 通往奥格瑞玛的道路 二分答案+最短路SPFA

洛谷P1462 通往奥格瑞玛的道路二分答案+最短路SPFA 二分交费最多的一次的钱数 然后只将符合要求的边加入图中 如果到终点的最短路大于等于血量 或者直接起点不能到达终点那么说明不符合要求 需要加大答案 时间复杂度 (log答案)* Ek 需要注意如果本来就不能到达 那么直接输出AFK 1 #include <bits/stdc++.h> 2 #define LL long long 3 #define For(i,j,k) for(int i=j;i<=k;i++) 4 using

[洛谷OJ] P1114 “非常男女”计划

洛谷1114 “非常男女”计划 本题地址:http://www.luogu.org/problem/show?pid=1114 题目描述 近来,初一年的XXX小朋友致力于研究班上同学的配对问题(别想太多,仅是舞伴),通过各种推理和实验,他掌握了大量的实战经验.例如,据他观察,身高相近的人似乎比较合得来. 万圣节来临之际,XXX准备在学校策划一次大型的“非常男女”配对活动.对于这次活动的参与者,XXX有自己独特的选择方式.他希望能选择男女人数相等且身高都很接近的一些人.这种选择方式实现起来很简单.