top1编程
← 返回题目
题解

指定队列

1 条题解

  • 0
    @ 2026-8-7 14:55:34

    PP4863 指定队列(基础)

    解题思路

    第一步,理解题意。 1 号同学先站在队伍里。接着 2 到 N 号同学依次来排队,每人给出 (k, p):p=0 表示站到 k 号同学的左边,p=1 表示站到右边。所有人都站好后,从左到右输出所有同学的编号。

    第二步,想到双向链表。 同学只会站到某个人左边或右边,这是典型的链表插入。我们用 pre 和 nxt 两个数组模拟双向链表:pre[i] 记 i 左边是谁,nxt[i] 记 i 右边是谁,用 0 表示"没有",再用 head 记住最左边是谁。

    第三步,分两种情况插入。 插到 k 右边时,先让 i 的左边指向 k、右边指向 k 原来的右边,再把前后两个邻居接上 i。插到 k 左边时类似,但要注意:如果 k 本来就在队头,head 要换成 i,否则 head 指向的就不是最左边的人了。

    第四步,例子验证。 样例 N=6:2 站 1 左边,3 站 1 右边,4 站 2 左边,5 站 3 右边,6 站 2 右边,最后队伍是 4 2 6 1 3 5,和输出完全一致。

    第五步,边界情况。 k 一定是比 i 小的编号,所以插入时 k 一定已经在队伍里,不会出现"找不到人"的情况;N 最大 10000,数组开 10005 足够。

    参考代码

    // 指定队列:每个同学插到指定同学的左边或右边,数组模拟双向链表
    #include <iostream>
    using namespace std;
    
    int pre[10005];  // pre[i] 表示 i 左边是谁,0 表示没有
    int nxt[10005];  // nxt[i] 表示 i 右边是谁,0 表示没有
    
    int main() {
        int n, k, p;
        cin >> n;
        int head = 1;  // 一开始队头是 1 号
        for (int i = 2; i <= n; ++i) {
            cin >> k >> p;
            if (p == 0) {
                // 插到 k 的左边
                nxt[i] = k;
                pre[i] = pre[k];
                if (pre[k] == 0) head = i;  // k 原来在队头,队头换成 i
                else nxt[pre[k]] = i;
                pre[k] = i;
            } else {
                // 插到 k 的右边
                pre[i] = k;
                nxt[i] = nxt[k];
                if (nxt[k] != 0) pre[nxt[k]] = i;
                nxt[k] = i;
            }
        }
        for (int i = head; i != 0; i = nxt[i]) {
            cout << i << " ";
        }
        cout << endl;
        return 0;
    }
    

    复杂度分析

    每个同学只做几次赋值就能完成插入,遍历输出也只要 O(N) 次,所以总时间复杂度 O(N),空间复杂度 O(N)。N 最大 10000,轻松通过。

    • 1