[USACO09OPEN] Cow Line S

GitHub跳转原题关系图返回列表

把当前牛队维护成双端队列,左右加入和左右删除都直接映射到 deque 的头尾操作。

OJ: luogu

题目 ID: P2952

难度:入门

标签:队列模拟USACO

日期: 2026-06-18 14:40

题意

有一列牛,初始为空。

牛会按编号 1,2,3,... 依次出现,每次新来的牛会加入左端或右端。

另外还会有删除操作,从左端或右端删去若干头牛。

要求输出所有操作结束后,队伍从左到右剩下的牛编号。

思路

先看最直接的做法:把当前队伍存在数组里。

  • 左边加入就插到开头;
  • 右边加入就插到末尾;
  • 左边删除就删掉开头若干个;
  • 右边删除就删掉末尾若干个。

这个暴力版最容易对应题目:

cpp
#include <bits/stdc++.h>
using namespace std;

int s;
int cur = 1;
char op, dir;
int k;
vector<int> line_cows;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> s;

    for (int i = 1; i <= s; i++) {
        cin >> op >> dir;

        if (op == 'A') {
            if (dir == 'L') {
                line_cows.insert(line_cows.begin(), cur);
            } else {
                line_cows.push_back(cur);
            }
            cur++;
        } else {
            cin >> k;
            while (k--) {
                if (dir == 'L') {
                    line_cows.erase(line_cows.begin());
                } else {
                    line_cows.pop_back();
                }
            }
        }
    }

    for (int x : line_cows) {
        cout << x << '\n';
    }

    return 0;
}

但如果用普通数组,头部插入和头部删除都可能搬动很多元素。

题目所有操作都只发生在两端,所以最合适的数据结构其实就是双端队列 deque

  • A L 对应 push_front
  • A R 对应 push_back
  • D L K 对应连续 Kpop_front
  • D R K 对应连续 Kpop_back

再用一个变量 cur 记录下一头要加入的牛编号即可。

代码

cpp
#include <bits/stdc++.h>
using namespace std;

int s;
int cur = 1;
char op, dir;
int k;
deque<int> q;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> s;

    for (int i = 1; i <= s; i++) {
        cin >> op >> dir;

        if (op == 'A') {
            if (dir == 'L') {
                q.push_front(cur);
            } else {
                q.push_back(cur);
            }
            cur++;
        } else {
            cin >> k;
            while (k--) {
                if (dir == 'L') {
                    q.pop_front();
                } else {
                    q.pop_back();
                }
            }
        }
    }

    for (int x : q) {
        cout << x << '\n';
    }

    return 0;
}

复杂度

  • 时间复杂度:O(S)O(S)
  • 空间复杂度:O(S)O(S)

总结

这题的重点不是推公式,而是把题面操作快速映射到正确的数据结构。

只要看到“所有修改都在两端进行”,就应该优先想到双端队列。