餐厅出入最少人数问题:贪心算法

张三最近开了家餐厅。张三想了解某一时段至少有多少不同人出入餐厅。张三就雇佣了一个人,呆在餐厅门口,有人进入餐厅,他就在纸上记下+,有人出餐厅,他就在纸上记下-。

假设同一时间没有两个人同时进入或出餐厅;也假设最初的时候餐厅中有足够多的人。问,这段时间里,至少有多少人出入餐厅。(不同人出入的顺序是随意的,一个人可以反复出入。)

Input

第一行是一个正整数m,表示有m组测试数据。(m<=100)

每组测试数据是一个由字符'+'和'-'组成的字符串。字符一个接一个,之间没有其他的分隔符,字符串的长度<=200。

Output

对于每一组测试数据输出一行,最少数量的人。

1 分析

这是一道非常经典的贪心算法 入门题,核心是通过最大化人员复用来最小化总人数。从问题本质、核心思路、算法步骤和代码实现四个方面来分析

一、问题本质拆解

首先明确几个关键前提和目标:

  1. 每个+对应一个进入事件 ,每个-对应一个离开事件
  2. 同一时间只有一个人进出,事件顺序严格对应
  3. 一个人可以反复出入(进入→离开→进入→离开...)
  4. 最初餐厅里有足够多的人,可以作为离开事件的"备用人员"
  5. 目标:给所有事件分配人员,满足"离开前必须在餐厅内"的规则,求最少需要多少不同的人

二、核心贪心思想

要让人数最少,就要尽可能让同一个人多次出入,而不是每次都用新人。我们可以把人分为两类:

  • 在餐厅内的人 :可以用来处理离开事件-
  • 在餐厅外的人 :可以用来处理进入事件+

当遇到一个事件时,优先复用已经使用过的人,只有当没有可用的复用人员时,才需要新增一个人。

三、算法步骤(O(n)时间复杂度)

我们只需要维护三个简单的计数变量,不需要复杂的数据结构:

  1. inside:当前在餐厅内的可用人数(已经进入过餐厅,还没离开)
  2. outside:当前在餐厅外的可用人数(已经离开餐厅,可以再次进入)
  3. min_people:最少需要的总人数

具体执行流程

遍历输入字符串的每个字符:

  • 遇到'+'(进入事件):

    1. 如果outside > 0:有已经出入过的人在外面,可以复用。将outside减1,inside加1。
    2. 否则:没有可用的复用人员,需要新增一个人。将min_people加1,inside加1。
  • 遇到'-'(离开事件):

    1. 如果inside > 0:有已经出入过的人在里面,可以复用。将inside减1,outside加1。
    2. 否则:没有可用的复用人员,需要新增一个最初就在餐厅里的人。将min_people加1,outside加1。

四、典型例子验证

例子1:输入"±-+"

步骤 字符 inside outside min_people 说明
初始 - 0 0 0 初始化
1 '+' 1 0 1 外面没人,新增1人
2 '-' 0 1 1 里面有人,复用出去
3 '-' 0 2 2 里面没人,新增1个最初的人
4 '+' 1 1 2 外面有人,复用进来

最终结果:2人,正确。

例子2:输入"+±-"

步骤 字符 inside outside min_people 说明
初始 - 0 0 0 初始化
1 '+' 1 0 1 新增1人
2 '+' 2 0 2 新增1人
3 '-' 1 1 2 复用1人出去
4 '-' 0 2 2 复用1人出去

最终结果:2人,正确。

例子3:输入"-+"

步骤 字符 inside outside min_people 说明
初始 - 0 0 0 初始化
1 '-' 0 1 1 里面没人,新增1个最初的人
2 '+' 1 0 1 外面有人,复用进来

最终结果:1人,正确。

五、代码实现C++

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 计算最少需要的人数
int calculateMinPeople(const string& s) {
    int inside = 0;    // 当前在餐厅内的可用人数(可离开)
    int outside = 0;   // 当前在餐厅外的可用人数(可进入)
    int minPeople = 0; // 最少总人数

    for (char c : s) {
        if (c == '+') { // 进入事件
            if (outside > 0) {
                // 优先复用已经出入过餐厅的人
                outside--;
                inside++;
            } else {
                // 没有可用的复用人员,新增一个人
                minPeople++;
                inside++;
            }
        } else { // 离开事件(c == '-')
            if (inside > 0) {
                // 优先复用已经在餐厅内的人
                inside--;
                outside++;
            } else {
                // 没有可用的复用人员,新增一个最初就在餐厅里的人
                minPeople++;
                outside++;
            }
        }
    }

    return minPeople;
}

int main() {
    int m;
    cin >> m; // 读取测试数据组数

    while (m--) {
        string s;
        cin >> s; // 读取每组的出入记录字符串
        cout << calculateMinPeople(s) << endl;
    }

    return 0;
}

六、关键易错点提醒

  1. 最初的人也要算:如果一个人最初就在餐厅里,在这段时间里他出去了,就算是"出入过餐厅"的人,需要计入总人数。
  2. 人员复用的顺序不影响结果:无论复用哪个人,最终的最少人数都是一样的,所以我们只需要计数,不需要跟踪具体是哪个人。
  3. 边界情况处理:输入全是'-'的情况(如"---"),答案就是字符串的长度,因为每个离开事件都需要一个最初的人。
相关推荐
朝朝辞暮i34 分钟前
C++ 第 31C 课:Subscriber 到底是什么
开发语言·c++·算法
Evand J1 小时前
【MATLAB例程】三维RRT+APF路径规划与AOA-TDOA融合定位算法。附代码的下载链接
算法·matlab·路径规划·rrt·tdoa·apf·aoa
海绵宝宝转agent2 小时前
leetCode100算法开源笔记分享
笔记·算法
写后端的胖头鱼2 小时前
时间复杂度 & 空间复杂度
java·数据结构·算法·时间复杂度·空间复杂度
hahaha60162 小时前
黑体轨迹和色温--光源白点
人工智能·嵌入式硬件·算法·计算机视觉
海绵宝宝转agent2 小时前
基于Redis ZSet+AOP+注解实现限流注解算法
数据库·redis·算法
All for pursuit.3 小时前
【回溯-7】494.目标和
数据结构·c++·算法·leetcode
白杨尚青3 小时前
C++入门篇(十三):vector(上)——动态数组:构造、空间增长与迭代器失效
开发语言·c++·笔记·算法·stl
Jasmine_llq3 小时前
《P17461 [GESP202609 八级] 生成树计数》
算法·栈·tarjan 算法·边双连通分量·深度优先搜索 dfs·tarjan 保存当前路径节点·模乘法
Jasmine_llq3 小时前
《P17462 [GESP202609 八级] 末班车》
算法·dijkstra 算法·最大化版本,求最长可行值·建反向图(逆图 / 反转边)·多源 dijkstra 思想·对每个终点单独跑一遍djst·离线预处理 + 在线查询