hiho一下第59周《Performance Log》题目分析

6
4

题意分析

给定一个单线程程序运行的记录,包含有每个函数启动和结束的时间。判定该份记录是否错误,主要的错误包含:

  • 记录中的时间不是单调递增的
  • 一个函数的结束时间比启动时间更早
  • 记录中一个函数有不对应的启动操作START或结束操作END,比如出现了START却没有对应的END,或出现了END却没有出现START。而函数的STARTEND应该成对出现
  • 两个函数出现交叉的情况,而在单线程程序中是不会出现的,比如 A START B START A END B END

算法分析

根据上面对可能出现错误的分析,我们可以分别对每一种错误进行处理:

记录中的时间不是严格递增的

对每一条记录的时间都与前一条的时间进行比较即可判定。

一个函数的结束时间比启动时间更早

对出现的STARTEND标记的时间直接进行计算即可判定。

不对应的STARTEND

统计每个函数STARTEND的个数是否相等即可判定

两个函数出现交叉

本题要做的是模拟一个函数调用栈,其实际考察的内容是对于的理解和运用。

我们将函数启动的操作视为进栈PUSH,函数结束的操作视为出栈POP,对于一个单线程的程序来说,其函数的调用一定满足栈的过程。在出现函数A中调用函数B的情况时,函数B的结束时间一定早于函数A。这正是栈过程中先进后出原则的体现。

如果我们用栈来模拟前面的例子,则有

  • A START

    Stack A

  • B START

    Stack A B

  • A END

    此时出现了错误,其操作为不在栈顶的A出栈。

对于正确的情况,比如:

A START B START B END A END

同样用栈来模拟时有:

  • A START

    Stack A

  • B START

    Stack A B

  • B END

    Stack A

  • A END

    Stack

因此对于第三类错误,我们需要在程序中使用栈来模拟整个过程,即可判定是否有出现错误。

总结

我们使用栈来模拟整个程序调用的过程:

首先对于每一个记录比较与前一条记录的时间。

当出现了START操作的函数直接进栈。

当出现了END操作的函数时,判定该函数是否就是栈顶的函数,若不是则表明该记录有错误。同时对于第二类操作中"出现了END却没有出现START"的情况也处理了。若出栈元素是栈顶元素时,我们在此时对其时间进行一次检查,就可以判定第一类错误。

当整个过程记录都使用栈模拟完毕后,我们还需要对当前栈内是否还有元素进行判定。若栈不为空,则出现第二类情况中"出现了START却没有对应的END"的情况。

此外在输出时,题目要求按照函数调用树深度优先的顺序依次输出每一个函数。在模拟栈的过程中,函数入栈的顺序也正是调用树的顺序,所以在处理过程中我们使用一个序列outputList来记录函数入栈的顺序,并在函数END操作时去更新该函数其运行时间。

其伪代码如下:

For i = 1 .. n
    If (i != 0 and log[i].time < log[i - 1].time)
        Return "Error"
    End If
    If log[i].Action == "START" Then
        Stack.Push(log[i])
        outputList.push(log[i].FuncName)    // 将该函数压入输出序列
    Else
        If (Stack.Size == 0 or Stack.Top.FuncName != log[i].FuncName)
            Return "Error"
        End If
        startLog = Stack.Pop()
        If startLog.Time > log[i].Time Then
            Return "Error"
        End If
        setTime(startLog.FuncName, log[i].Time - startLog.Time)
        // 记录outputList中名称为startLog.FuncName的函数的运行时间
    End If
End For

结果分析

在实际的比赛中,该题目的通过率为14%。

在选手的程序中主要出现的错误有:

  • 由于本题涉及了时间格式,在时间输入输出上出现问题,导致时间计算出问题
  • 未判定函数开始时间是否大于结束时间
  • 模拟栈结束后未检查栈是否为空,很多选手都是因为这个原因而没有得到100分
  • 为什么要未判定函数开始时间是否大于结束时间

  • 因为有可能出现这种错误,而函数开始的时刻应该是小于结束时刻的。

  • 不是已经是一个时间递增的序列了?

  • 被“严格递增”坑了,实际数据并不是严格递增的,有相等的情况!!!

  • 而且我怀疑数据有没有考虑ABCCBADD这种错误情况。

  • ABCCBADD这个怎么错了?

  • ABCCBADD并没有什么问题啊。

  • 我是这样理解的,ABCCBADD,单线程那么A应该是最外层函数(main)吧,A都结束了,怎么还可以有D函数运行?

  • 添加评论
  • reply

7 answer(s)

0

这。。。。。。。。

0

是不是后面的时间都比前面的时间多1

0

循环调用算不算错呢? 比如

A  START
B START
A START
...
  • 算吧。题目中说了每个函数只能被调用一次。

  • 一个函数可以被多次调用,根据我的程序的提交结果来看,从12.5分到62.5分,一次修改了以下点,然后每次多12.5分:最后栈为空错,结束时间不大于开始时间为错,只按照一次处理改成可以按照多次处理,供参考; 然后就改不动了,不知道哪里有问题,至于说A开始不是A结束的,我试了,没有效果,也就是这个case的坑不存在

  • 添加评论
  • reply
1

如果能看到那些cases失败了就好了。貌似情况都考虑了,就是到不了100

4

函数调用时间的HH部分有大于24小时的情况,一开始用了JDK的SimpleDateFormat来处理输出时间总有一组数据过不去,真TM坑,用java的同学注意下。

  • 如果这样的话,[TimeStamp] format is hh:mm:ss这样描述就不太准确了。:(

  • 果然是这个问题啊,用C#图一个方便使用了DateTime,用int转化一下 AC了。

  • 添加评论
  • reply
3

相邻操作可以是同一时间,单线程这样好吗~~纠结了半天!!

3

在出栈的时候没有必要比较 函数开始的时间比较结束的时间是否大 因为在先前 已经保证了 输入数据的时间是递增的 因此 此时输入的数据的时间 应该比栈中任何一条记录的时间都大

write answer 切换为英文 切换为中文


转发分享