P2952 [USACO09OPEN] Cow Line S
题目描述
Farmer John(以下简称 FJ)的 $ N $ 头奶牛(用 $ 1 \dots N $ 编号)在直线上排队。一开始,这条线上没有任何奶牛,随着时间的推移,奶牛们会一个接一个地站到队伍的左边或右边。又过了一会儿,某些奶牛会从队伍里离开,去吃自己最喜欢的草料。
FJ 无法跟踪每一头奶牛,于是,他想让你来帮助他。
奶牛以 $ 1 \dots N $ 的顺序排队,并且离开的奶牛不会再次回来。数据将会给出 $ S ((( 1 \le S \le 100000 $) 条指令,各占一行,分两种:
- $ 1 $ 头奶牛加入了队列(还有一个参数,表示从左加入还是从右加入);
- $ K $ 头奶牛从左边或者右边离开了队列(还有两个参数,分别表示从左离开还是从右离开和离开多少头奶牛)。
输入的命令一定是可以执行的。
所有的操作结束后,你的程序应该以从左到右的顺序输出这个奶牛队列。数据保证最后的队列不空。
输入格式
- 第 $ 1 $ 行:单独一个整数 $ S $。
- 第 $ 2 \dots S+1 $ 行:第 $ i+1 $ 行会有一条命令,有以下几种:
A L
:一头奶牛从队列左边加入;A R
:一头奶牛从队列右边加入;D L K
:$ K $ 头奶牛从队伍左边离开;D R K
:$ K $ 头奶牛从队伍右边离开。
输出格式
- 第 $ 1 \dots ?? $ 行:从左到右输出最后的奶牛队列,一个奶牛编号占一行。
【样例解释】
以下为输入的命令及对应的队列:
A L
:$ 1 $;A L
:$ 2,1 $;A R
:$ 2,1,3 $;A L
:$ 4,2,1,3 $;D R 2
:$ 4,2 $;A R
:$ 4,2,5 $;A R
:$ 4,2,5,6 $;D L 1
:$ 2,5,6 $;A L
:$ 7,2,5,6 $;A R
(最终序列):$ 7,2,5,6,8 $。
输入输出样例 #1
输入 #1
10
A L
A L
A R
A L
D R 2
A R
A R
D L 1
A L
A R
输出 #1
7
2
5
6
8
说明/提示
Input Resulting Cow Line
A L 1
A L 2 1
A R 2 1 3
A L 4 2 1 3
D R 2 4 2
A R 4 2 5
A R 4 2 5 6
D L 1 2 5 6
A L 7 2 5 6
A R 7 2 5 6 8
感谢@ ws_fuweidong 提供翻译。
解题思路
本题需要模拟一个双端队列的操作,奶牛可以从左侧或右侧加入队列,也可以从左侧或右侧离开队列。关键点在于高效地处理双端操作,并最后输出队列中剩余的奶牛编号。
核心思路:
- 数据结构选择:使用双端队列(deque)来存储奶牛编号,支持高效的头尾插入和删除操作。
- 奶牛编号管理:使用一个递增的计数器(
id
)为每头新加入的奶牛分配唯一编号。 - 指令处理:
A L
:在队列头部插入新奶牛A R
:在队列尾部插入新奶牛D L K
:从队列头部移除K头奶牛D R K
:从队列尾部移除K头奶牛
- 结果输出:按从左到右顺序(即队列头到尾)输出剩余奶牛编号。
C++代码实现
#include <iostream>
#include <deque>
using namespace std;
int main() {
int S;
cin >> S;
deque<int> dq; // 双端队列存储奶牛编号
int id = 1; // 当前可用的奶牛编号
for (int i = 0; i < S; i++) {
char op, dir;
cin >> op;
if (op == 'A') { // 加入操作
cin >> dir;
if (dir == 'L') {
dq.push_front(id++); // 左侧加入
} else if (dir == 'R') {
dq.push_back(id++); // 右侧加入
}
} else if (op == 'D') { // 删除操作
int k;
cin >> dir >> k;
if (dir == 'L') {
// 从左侧移除k头奶牛
while (k-- && !dq.empty()) {
dq.pop_front();
}
} else if (dir == 'R') {
// 从右侧移除k头奶牛
while (k-- && !dq.empty()) {
dq.pop_back();
}
}
}
}
// 输出最终队列(从左到右)
for (int cow : dq) {
cout << cow << '\n';
}
return 0;
}
代码说明
- 数据结构:
deque<int> dq
:双端队列,支持O(1)时间复杂度的头尾插入和删除int id
:当前可用的奶牛编号,初始为1,每次加入后自增
- 指令处理:
- 加入操作:
A L
:dq.push_front(id++)
(左侧加入)A R
:dq.push_back(id++)
(右侧加入)
- 删除操作:
D L K
:循环k
次dq.pop_front()
D R K
:循环k
次dq.pop_back()
- 删除前检查队列非空(
!dq.empty()
)确保安全
- 加入操作:
- 输出:
- 使用范围for循环遍历双端队列,按从左到右顺序输出编号
- 输出使用
'\n'
确保换行
复杂度分析
- 时间复杂度:O(S),每条指令处理时间都是O(1)(删除操作中K的总和不超过S)
- 空间复杂度:O(N),N是队列中最多同时存在的奶牛数