最大重叠事件数的持续时间

Duration of maximum number of overlapping events

本文关键字:持续时间 事件 重叠      更新时间:2023-10-16

在尝试提高我的算法技能时,我发现自己陷入了以下问题,简而言之,它要求您找到房间中存在最多人数的时间跨度的持续时间:

https://jutge.org/problems/P27158_en

我提出的解决方案可以正确解决网站建议的所有公共测试用例的问题,但对于一个或多个隐藏的私人测试用例,它失败了。

我的解决方案在 std::vector 中为每个事件保存两个条目:一个用于到达,一个用于离开,每个条目由 [eventtype, eventtime] 组成。然后按事件时间对向量进行排序,最后遍历向量以确定具有最大来宾数的时间跨度的持续时间。我的代码如下:

            #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    enum class EventType {ARRIVE, LEAVE};
    struct Event{
        int time;
        EventType type;
        Event(){}
        Event(int tm, EventType t) : time(tm), type (t){}
       // inline bool operator<(const Event& e) const {return time < e.time || (time == e.time && type==EventType::LEAVE);}
    };
    bool eventCompare(const Event& e1, const Event& e2) {
        if(e1.time < e2.time){
            return true;
        } else if(e1.time == e2.time) {
            if(e1.type == EventType::LEAVE && e2.type == EventType::ARRIVE) {
                return true;
            } else  {
                return false;
            }
        } else {
            return false;
        }
    }
    int main()
    {
        int visits;
        int timeStart, timeEnd;
        int maxVisits, maxDuration, localMaxVisits, localStartTime, localEndTime, duration;
        std::vector<Event> events;
        std::vector<Event>::const_iterator it;
        // Get each Case from stdin.
        // 0 is the special stopping case.
        while(cin>>visits && visits!=0) {
            events.clear();
            // Read all the visits, for each one save two entries to the visits-vector,
            // one for the arrival time another for the leaving time.
            for(int i=1; i<=visits; ++i){
                cin>>timeStart>>timeEnd;
                Event eStart(timeStart, EventType::ARRIVE);
                Event eEnd(timeEnd, EventType::LEAVE);
                events.push_back(eStart);
                events.push_back(eEnd);
            }
            // Sorting the vector so that events are ordered by their arrival time.
            // In case two events coincide on arrival time, we place leaving events before arrival events.
            std::sort(events.begin(), events.end(), eventCompare);
            maxVisits=0;
            maxDuration=0;
            localMaxVisits=0;
            localStartTime=0;
            localEndTime=0;
            // Loop through the sorted vector
            for(it=events.begin(); it!=events.end(); ++it){
                // If the event type is arrival
                if((*it).type == EventType::ARRIVE) {
                    // We only reset the localStartTime if there is no
                    // gap between the last endtime and the current start time
                    // For example two events 11-13 and 13-15 should be treated as one long event
                    // with duration 11-15
                    if(localEndTime < (*it).time) {
                        localStartTime = (*it).time;
                    }
                    localMaxVisits++;
                } else { // Event type leave
                    // Calculate the duration
                    duration = (*it).time - localStartTime;
                    // If we have a new max in overlaps or equal overlaps but larger duration than previous
                    // We save as the global max.
                    if(localMaxVisits > maxVisits || (localMaxVisits == maxVisits && duration>maxDuration)) {
                        maxVisits=localMaxVisits;
                        maxDuration = duration;
                    }
                    localMaxVisits--;
                    localEndTime= (*it).time;
                }
            }
            cout<<maxVisits<<" "<<maxDuration<<endl;
        }
        return 0;
    }

我已经根据@j_random_hacker的评论修改了代码,程序现在不会像以前那样超过隐藏的私人测试用例的时间限制,但现在得到一个判断"错误的答案"(仅适用于私人测试用例)。我将尝试找出算法错误可能是什么。感谢所有回答的人。

我将比较函数更改为此,TLE 消失了(并更改为 WA):

bool operator< ( const Event& e ) const
{
    return (time < e.time) || (time == e.time && type==EventType::LEAVE && e.type != EventType::LEAVE);
}

正如评论中所建议的,原因是您给出的比较函数没有严格的弱排序。(即使两个对象具有相同的time和相同的typeEventType::LEAVE),它也返回true,而当两个对象等效时,它应该返回 false。

编辑:

由于以下测试用例,您获得

WA:

5 1 3 1 3 3 4 4 7 4 7

您的输出 - 2 6
正确的输出 - 2 3

您正确获得了最大事件数,但它们的持续时间是错误的。

对此的补救措施是在两次迭代中计算答案。
在第一次迭代中,我们计算最大事件数。
在第二次迭代中,我们使用第一次迭代中获得的结果计算最大持续时间。

正确的代码(几乎与您的代码相似):

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
enum class EventType {ARRIVE, LEAVE};
struct Event
{
    int time;
    EventType type;
    Event() {}
    Event ( int tm, EventType t ) : time ( tm ), type ( t ) {}
    bool operator< ( const Event& e ) const
    {
        return ( time < e.time ) || ( time == e.time && type == EventType::LEAVE &&
                                      e.type != EventType::LEAVE );
    }
};
int main()
{
    int visits;
    int timeStart, timeEnd;
    int maxVisits, maxDuration, localMaxVisits, localStartTime, localEndTime,
         duration;
    std::vector<Event> events;
    std::vector<Event>::const_iterator it;
    // Get each Case from stdin.
    // 0 is the special stopping case.
    while ( cin >> visits && visits != 0 )
    {
        events.clear();
        // Read all the visits, for each one save two entries to the visits-vector,
        // one for the arrival time another for the leaving time.
        for ( int i = 1; i <= visits; ++i )
        {
            cin >> timeStart >> timeEnd;
            Event eStart ( timeStart, EventType::ARRIVE );
            Event eEnd ( timeEnd, EventType::LEAVE );
            events.push_back ( eStart );
            events.push_back ( eEnd );
        }
        // Sorting the vector so that events are ordered by their arrival time.
        // In case two events coincide on arrival time, we place leaving events before arrival events.
        std::sort ( events.begin(), events.end() );
        maxVisits = 0;
        localMaxVisits = 0;
        // Find `maxVisits`
        for ( it = events.begin(); it != events.end(); ++it )
        {
            if ( ( *it ).type == EventType::ARRIVE )
            {
                localMaxVisits++;
            }
            else
            {
                maxVisits = max ( maxVisits, localMaxVisits );
                localMaxVisits--;
            }
        }

        maxDuration = 0;
        localStartTime = 0;
        localEndTime = 0;
        // Now calculate `maxDuration`
        for ( it = events.begin(); it != events.end(); ++it )
        {
            if ( ( *it ).type == EventType::ARRIVE )
            {
                localMaxVisits++;
                if ( localMaxVisits == maxVisits && localEndTime < ( *it ).time )
                {
                    localStartTime = ( *it ).time;
                }
            }
            else
            {
                duration = ( *it ).time - localStartTime;
                if ( localMaxVisits == maxVisits )
                {
                    localEndTime = ( *it ).time;
                    maxDuration = max ( maxDuration, duration );
                }
                localMaxVisits--;
            }
        }
        cout << maxVisits << " " << maxDuration << endl;
    }
}

您可以通过完全取消 Event 类并改用 pair<int,int> 来简化代码。配对按其第一个元素(这将是您的事件时间)排序,然后按其第二个元素排序。我建议用类似于以下内容的代码填充您的向量:

    vector<pair<int,int>> v;
    for (int i=0; i<n; i++) { // n events to add
        int a, b;
        cin >> a >> b;            
        v.push_back({a, 1});  // 1 for "enter"
        v.push_back({b, -1}); // -1 for "leave"
    }
    sort(v.begin(), v.end());

如您所见,排序不需要定义任何额外的内容。它只是工作(tm)。

两个陷阱:

    如果我到达 t0,
  • 而其他人在 t0 离开,则队伍中的人数没有变化。
  • 如果
  • 许多人在同一时间到达或离开,您应该只重新计算一次援助变化(如果它是 0,则完全忽略它,就像前面的陷阱一样)。

还可以确认,对于这个特定问题,法官不会接受任何使用复杂结构的答案(我最初试图用map<int, int>来解决它)。简单执行

    map<int,int> m;
    for (int i=0; i<n; i++) { // n events to add
        int a, b;
        cin >> a >> b;            
        m[a]++;  // add 1 party-goer at time a
        m[b]--;  // remove 1 at time b
    }

让你超过时间限制(即使它会自动排序)。因此,即使间隔树非常适合重复的间隔查询,它们也不是解决此特定问题(仅查询一次)的正确工具。

祝你好运,为那个难以捉摸的AC修复你的代码!我可以确认这是可以实现的