P1160 队列安排题解

题目传送门

一、双链表示意图

二、C++代码

#include <bits/stdc++.h>

using namespace std;

//双链表模板
const int N = 100010;

int e[N]; //数据
int l[N]; //左链表
int r[N]; //右链表
int idx;  //准备放入的索引值

//正整数x,表示将x号同学从队列中移去,如果x号同学已经不在队列中则忽略这一条指令。
//正是因为有了这句话,如果没有删除的标识了,真的有重复指令就会让数据出问题。
// 因为可能重复的对左右结点进行操作,造成数据错误。
int st[N]; //删除标识
// 黄海写的题解
// https://www.acwing.com/solution/content/35794/

//0表示头,1表示尾,正式使用的是从idx=2开始
void init() {
    //0的右端是1,1的左端是0
    r[0] = 1, l[1] = 0, idx = 2;
}

//函数含义:在k号节点右侧插入,值为x
//如果想实现:在k号节点左侧插入,值为x  则调用:add(l[k],x),意义为在k的左侧,
//相当于在k的左侧节点的右侧,
void add(int k, int x) {
    e[idx] = x; //将x放入到一个空的单元格中
    l[idx] = k; //将新增加的单元格的右指针指向k的下一链路节点
    r[idx] = r[k];  //将新增加的单元格的左指针指向k
    //完成上面两步后,只是让idx这个节点建立了与原来两个节点的关系,而原来的两个k与r[k],
    // l[r[k]]还是旧的值,也需要进行修改。
    l[r[k]] = idx;
    r[k] = idx;
    //为下一次做准备
    idx++;
}

//删除第k个节点
void remove(int k) {
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}

int n, k, p, m, x;


//输出双链表
void print() {
    //遍历双向链表的方法
    for (int i = r[0]; i != 1; i = r[i]) cout << e[i] << " ";
    cout << endl;
}

int main() {
    //初始化双链表
    init();

    //读入
    cin >> n;

    //先将1号同学安排进队列
    //最左端就是表示在head(0号)的右侧插入一个数据
    add(0, 1);
    for (int i = 2; i <= n; i++) {
        //k为小于i的正整数
        cin >> k >> p;
        //p为0,表示将i号同学插入到k号同学的左边
        //p为1,表示将i号同学插入到k号同学的右边

        //idx从2开始,所以下标需要k+1传入才对
        //就是1号同学的idx=2
        //就是2号同学的idx=3
        //就是3号同学的idx=4
        //就是k号同学的idx=k+1
        //右边就是正常的add(k+1,i)
        //左边稍有一点变化:add(l[k+1],i), 表示在当前号的左侧那个,就是它的前面结点的右侧放入
        if (p == 0) add(l[k + 1], i);
        else add(k + 1, i);
    }
    //表示去掉的同学数目
    cin >> m;
    for (int i = 1; i <= m; i++) {
        cin >> x;
        //表示将x号同学从队列中移去
        if (!st[x + 1]) {
            remove(x + 1);
            st[x + 1] = 1;
        }
    }
    //输出链表 (表示了队列从左到右所有同学的编号,行末换行且无空格)
    print();
    return 0;
}
原文地址:https://www.cnblogs.com/littlehb/p/15074916.html