题解:洛谷 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

【核心思想】

  1. 问题分析:给定 \(S\) 条指令,支持从队列左端或右端加入奶牛(按 \(1\)\(N\) 顺序编号),以及从队列左端或右端删除 \(K\) 头奶牛。所有操作结束后输出剩余队列。这是一个双端队列(Deque)模拟问题,关键在于使用支持两端 \(O(1)\) 插入删除的数据结构。

  2. 算法选择

    • 双端队列(Deque):支持队首和队尾的 \(O(1)\) 插入(push_front/push_back)和删除(pop_front/pop_back
    • 顺序编号维护:用变量 mark\(1\) 开始递增,每次加入一头奶牛时分配当前编号
  3. 关键步骤

    • 读取输入\(S\)\(S\) 条指令
    • 初始化deque<int> dqmark = 1
    • 处理每条指令
      • A Ldq.push_front(mark++)(左端加入)
      • A Rdq.push_back(mark++)(右端加入)
      • D L K:循环 \(K\)dq.pop_front()(左端删除 \(K\) 头)
      • D R K:循环 \(K\)dq.pop_back()(右端删除 \(K\) 头)
    • 输出:从左到右遍历 dq,输出每个元素
  4. 时间/空间复杂度

    • 时间复杂度:\(O(S + N)\),每条指令 \(O(1)\),输出 \(O(N)\)
    • 空间复杂度:\(O(N)\),双端队列存储剩余奶牛
  5. 双端队列的核心思想

    • 两端操作的对称性:双端队列完美契合题目要求的左右两端加入和删除操作,所有操作均为 \(O(1)\)
    • 顺序编号的隐式维护:奶牛按加入顺序自动获得递增编号,无需额外管理,离队后不会复用
    • 动态数组实现std::deque 内部采用分块数组实现,避免普通数组的头部删除 \(O(N)\) 问题
    • 适用于"两端动态插入删除"类问题,核心是选择支持双端操作的数据结构

【解题思路】

img/Pasted image 20260519130041.png

【算法标签】

普及- #队列

【代码详解】

#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
posted @ 2026-08-28 15:56  团爸讲算法  阅读(5)  评论(0)    收藏  举报