题解:洛谷 P2952 Cow Line
【题目来源】
洛谷: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 ??\) 行:从左到右输出最后的奶牛队列,一个奶牛编号占一行。
【输入样例】
10
A L
A L
A R
A L
D R 2
A R
A R
D L 1
A L
A R
【输出样例】
7
2
5
6
8
【核心思想】
-
问题分析:给定 \(S\) 条指令,支持从队列左端或右端加入奶牛(按 \(1\) 到 \(N\) 顺序编号),以及从队列左端或右端删除 \(K\) 头奶牛。所有操作结束后输出剩余队列。这是一个双端队列(Deque)模拟问题,关键在于使用支持两端 \(O(1)\) 插入删除的数据结构。
-
算法选择:
- 双端队列(Deque):支持队首和队尾的 \(O(1)\) 插入(
push_front/push_back)和删除(pop_front/pop_back) - 顺序编号维护:用变量
mark从 \(1\) 开始递增,每次加入一头奶牛时分配当前编号
- 双端队列(Deque):支持队首和队尾的 \(O(1)\) 插入(
-
关键步骤:
- 读取输入:\(S\) 和 \(S\) 条指令
- 初始化:
deque<int> dq,mark = 1 - 处理每条指令:
A L:dq.push_front(mark++)(左端加入)A R:dq.push_back(mark++)(右端加入)D L K:循环 \(K\) 次dq.pop_front()(左端删除 \(K\) 头)D R K:循环 \(K\) 次dq.pop_back()(右端删除 \(K\) 头)
- 输出:从左到右遍历
dq,输出每个元素
-
时间/空间复杂度:
- 时间复杂度:\(O(S + N)\),每条指令 \(O(1)\),输出 \(O(N)\)
- 空间复杂度:\(O(N)\),双端队列存储剩余奶牛
-
双端队列的核心思想:
- 两端操作的对称性:双端队列完美契合题目要求的左右两端加入和删除操作,所有操作均为 \(O(1)\)
- 顺序编号的隐式维护:奶牛按加入顺序自动获得递增编号,无需额外管理,离队后不会复用
- 动态数组实现:
std::deque内部采用分块数组实现,避免普通数组的头部删除 \(O(N)\) 问题 - 适用于"两端动态插入删除"类问题,核心是选择支持双端操作的数据结构
【解题思路】

【算法标签】
普及- #队列
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int s;
int main()
{
cin >> s; // 输入s
deque<int> dq; // 定义双端队列dq
int mark=1; // 定义压入编号,起始为1
for (int i=1; i<=s; i++) { // 遍历s个命令
char act, d;
int k;
cin >> act; // 输入操作
if (act=='A') { // 如果是A
cin >> d; // 再输入方向
if (d=='L') dq.push_front(mark++); // 如果方向是L,则从队头插入数据
else dq.push_back(mark++); // 否则从队尾插入数据
} else { // 如果是D
cin >> d >> k; // 输入方向和k
for (int j=1; j<=k; j++) { // 遍历k次
if (d=='L') dq.pop_front(); // 如果是L则从队头删除数据
else dq.pop_back(); // 否则从队尾删除数据
}
}
}
for (int i=0; i<dq.size(); i++) { // 按顺序打印dq中所有剩余编号
cout << dq.at(i) << endl;
}
return 0;
}
【运行结果】
10
A L
A L
A R
A L
D R 2
A R
A R
D L 1
A L
A R
7
2
5
6
8
浙公网安备 33010602011771号