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

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

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

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. 边界情况处理:输入全是'-'的情况(如"---"),答案就是字符串的长度,因为每个离开事件都需要一个最初的人。
相关推荐
想做小南娘,发现自己是女生喵21 分钟前
【无标题】
数据结构·算法
Kx_Triumphs2 小时前
HDU4348 To the moon(主席树区间修改模板)
算法·题解
旖-旎2 小时前
《LeetCode647 回文子串 || LeetCode 5 最长回文子串》
c++·算法·leetcode·动态规划·哈希算法
轻颂呀3 小时前
约瑟夫环问题
算法
科技大视界5 小时前
投资AI项目,传统尽调不够用了——李章虎律师拆解算法、数据、算力三大雷区
人工智能·算法·数据挖掘
郝学胜-神的一滴5 小时前
算法实战:最小k个数——大顶堆的优雅解法
开发语言·数据结构·c++·python·程序人生·算法·排序算法
Irissgwe5 小时前
算法滑动窗口
数据结构·算法
怪兽学LLM6 小时前
LeetCode 105. 从前序与中序遍历序列构造二叉树:分治递归思路详解
算法·leetcode·职场和发展
可编程芯片开发6 小时前
基于PI控制的三相整流器控制系统的simulink建模与仿真,包含超级电容充电和电机
算法