【洛谷P1160 队列安排】题目分析
洛谷题目链接:P1160
\(\color{yellow}黄\),标准的双向链表
可能有点简单了,就当复习基础功
OK 那我们直接开刷
题目
题目描述
一个学校里老师要将班上 \(N\) 个同学排成一列,同学被编号为 \(1\sim N\),他采取如下的方法:
-
先将 \(1\) 号同学安排进队列,这时队列中只有他一个人;
-
\(2\sim N\) 号同学依次入列,编号为 \(i\) 的同学入列方式为:老师指定编号为 \(i\) 的同学站在编号为 \(1\sim(i-1)\) 中某位同学(即之前已经入列的同学)的左边或右边;
-
从队列中去掉 \(M\) 个同学,其他同学位置顺序不变。
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。
输入格式
第一行一个整数 \(N\),表示了有 \(N\) 个同学。
第 \(2\sim N\) 行,第 \(i\) 行包含两个整数 \(k,p\),其中 \(k\) 为小于 \(i\) 的正整数,\(p\) 为 \(0\) 或者 \(1\)。若 \(p\) 为 \(0\),则表示将 \(i\) 号同学插入到 \(k\) 号同学的左边,\(p\) 为 \(1\) 则表示插入到右边。
第 \(N+1\) 行为一个整数 \(M\),表示去掉的同学数目。
接下来 \(M\) 行,每行一个正整数 \(x\),表示将 \(x\) 号同学从队列中移去,如果 \(x\) 号同学已经不在队列中则忽略这一条指令。
输出格式
一行,包含最多 \(N\) 个空格隔开的整数,表示了队列从左到右所有同学的编号。
输入输出样例 #1
输入 #1
4
1 0
2 1
1 0
2
3
3
输出 #1
2 4 1
说明/提示
【样例解释】
将同学 \(2\) 插入至同学 \(1\) 左边,此时队列为:
2 1
将同学 \(3\) 插入至同学 \(2\) 右边,此时队列为:
2 3 1
将同学 \(4\) 插入至同学 \(1\) 左边,此时队列为:
2 3 4 1
将同学 \(3\) 从队列中移出,此时队列为:
2 4 1
同学 \(3\) 已经不在队列中,忽略最后一条指令
最终队列:
2 4 1
【数据范围】
对于 \(20\%\) 的数据,\(1\leq N\leq 10\)。
对于 \(40\%\) 的数据,\(1\leq N\leq 1000\)。
对于 \(100\%\) 的数据,\(1<M\leq N\leq 10^5\)。
思路
用一个双向链表来操作
直接创建一个结构体,其中包含:
-
左边、右边的同学的学号\((l, r)\)
-
自身的学号\((ki)\)
这样插入和删除的操作都可以用\(O(1)\)的时间复杂度来解决了
但是每次有新命令时,我们总不能把整个队列给遍历一遍来找到需要的学号吧...
所以我们就可以开一个\(数组(pos)\)来记录每个学号的同学的位置,0就是未入队
插入的时候,就分别记录下这名同学的学号就ok了
接下来分开说下每种操作的做法
首先进行定义,并进行初始化
const int maxn = 1e5 + 5;
int len = 0, pos[maxn]; //len总长度,pos[]每个同学的位置
struct node {
int ki, l, r;
node(int _ki = 0, int _l = 0, int _r = 0) //结构体初始化
{ki = _ki; l = _l; r = _r; }
} s[maxn];
初始链表:
| ki | l | r |
|---|---|---|
| s[ki].l | s[s[ki].l].l | ki |
| ki\(\mathbf{(now)}\) | s[ki].l | s[ki].r |
| s[ki].r | ki | s[s[ki].r].r |
- 将y号同学插入至x号同学左边:
void ins_left(int x, int y) { //x为原先学号,y为插入学号
int now = pos[x]; //定义now为原先学号的位置
s[++len] = node(y, s[now].l, now); //新人入队,ki = y,l=s[now].l,r=now(见上表)
s[s[now].l].r = len;
s[now].l = len; //更改原先学号的l和r
pos[y] = len; //标记插入学号的位置
}
- 将y号同学插入至x号同学右边:
void ins_right(int x, int y) { //大致同上
int now = pos[x];
s[++len] = node(y, now, s[now].r);
s[s[now].r].l = len;
s[now].r = len;
pos[y] = len;
}
-
删除x号同学:
删除有两种做法:
-
将x号同学出队(展示的也是这种方法)
-
标记x号同学,最终输出的时候直接跳过
-
void del(int x) {
int now = pos[x];
int li = s[now].l, ri = s[now].r;
s[li].r = ri;
s[ri].l = li; //将左右两人相连
pos[x] = 0; //x号同学位置标记为0
}
开始将\(node(0, 0, 0)\)存入列表,然后把\(1号放在0号后(ins\)_\(right(0, 1))\),再从2号遍历到n号
输出时,就从\(s[0].nxt\)开始输,一直到\(s[n].nxt=0\)为止就好了
附上完整代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n, m, len = 0, pos[maxn];
struct node {
int ki, l, r;
node(int _ki = 0, int _l = 0, int _r = 0)
{ki = _ki; l = _l; r = _r; }
} s[maxn];
void ins_left(int x, int y) {
int now = pos[x];
s[++len] = node(y, s[now].l, now);
s[s[now].l].r = len;
s[now].l = len;
pos[y] = len;
}
void ins_right(int x, int y) {
int now = pos[x];
s[++len] = node(y, now, s[now].r);
s[s[now].r].l = len;
s[now].r = len;
pos[y] = len;
}
void del(int x) {
int now = pos[x];
int li = s[now].l, ri = s[now].r;
s[li].r = ri;
s[ri].l = li;
pos[x] = 0;
}
int main() {
s[0] = node(0, 0, 0);
ins_right(0, 1);
scanf("%d", &n);
for(int i=2; i<=n; i++) {
int k, p;
scanf("%d%d", &k, &p);
p ? ins_right(k, i) : ins_left(k, i);
}
scanf("%d", &m);
for(int i=1; i<=m; i++) {
int x;
scanf("%d", &x);
if(pos[x])del(x);
}
int now = s[0].r;
while(now) {
printf("%d ", s[now].ki);
now = s[now].r;
}
return 0;
}
浙公网安备 33010602011771号