
张三最近开了家餐厅。张三想了解某一时段至少有多少不同人出入餐厅。张三就雇佣了一个人,呆在餐厅门口,有人进入餐厅,他就在纸上记下+,有人出餐厅,他就在纸上记下-。
假设同一时间没有两个人同时进入或出餐厅;也假设最初的时候餐厅中有足够多的人。问,这段时间里,至少有多少人出入餐厅。(不同人出入的顺序是随意的,一个人可以反复出入。)
Input
第一行是一个正整数m,表示有m组测试数据。(m<=100)
每组测试数据是一个由字符'+'和'-'组成的字符串。字符一个接一个,之间没有其他的分隔符,字符串的长度<=200。
Output
对于每一组测试数据输出一行,最少数量的人。
1 分析
这是一道非常经典的贪心算法 入门题,核心是通过最大化人员复用来最小化总人数。从问题本质、核心思路、算法步骤和代码实现四个方面来分析
一、问题本质拆解
首先明确几个关键前提和目标:
- 每个
+对应一个进入事件 ,每个-对应一个离开事件 - 同一时间只有一个人进出,事件顺序严格对应
- 一个人可以反复出入(进入→离开→进入→离开...)
- 最初餐厅里有足够多的人,可以作为离开事件的"备用人员"
- 目标:给所有事件分配人员,满足"离开前必须在餐厅内"的规则,求最少需要多少不同的人
二、核心贪心思想
要让人数最少,就要尽可能让同一个人多次出入,而不是每次都用新人。我们可以把人分为两类:
- 在餐厅内的人 :可以用来处理离开事件
- - 在餐厅外的人 :可以用来处理进入事件
+
当遇到一个事件时,优先复用已经使用过的人,只有当没有可用的复用人员时,才需要新增一个人。
三、算法步骤(O(n)时间复杂度)
我们只需要维护三个简单的计数变量,不需要复杂的数据结构:
inside:当前在餐厅内的可用人数(已经进入过餐厅,还没离开)outside:当前在餐厅外的可用人数(已经离开餐厅,可以再次进入)min_people:最少需要的总人数
具体执行流程
遍历输入字符串的每个字符:
-
遇到'+'(进入事件):
- 如果
outside > 0:有已经出入过的人在外面,可以复用。将outside减1,inside加1。 - 否则:没有可用的复用人员,需要新增一个人。将
min_people加1,inside加1。
- 如果
-
遇到'-'(离开事件):
- 如果
inside > 0:有已经出入过的人在里面,可以复用。将inside减1,outside加1。 - 否则:没有可用的复用人员,需要新增一个最初就在餐厅里的人。将
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;
}
六、关键易错点提醒
- 最初的人也要算:如果一个人最初就在餐厅里,在这段时间里他出去了,就算是"出入过餐厅"的人,需要计入总人数。
- 人员复用的顺序不影响结果:无论复用哪个人,最终的最少人数都是一样的,所以我们只需要计数,不需要跟踪具体是哪个人。
- 边界情况处理:输入全是'-'的情况(如"---"),答案就是字符串的长度,因为每个离开事件都需要一个最初的人。