cen114514

导航

【洛谷P1160 队列安排】题目分析

洛谷题目链接:P1160

\(\color{yellow}黄\)标准的双向链表
可能有点简单了,就当复习基础功
OK 那我们直接开刷


题目

题目描述

一个学校里老师要将班上 \(N\) 个同学排成一列,同学被编号为 \(1\sim N\),他采取如下的方法:

  1. 先将 \(1\) 号同学安排进队列,这时队列中只有他一个人;

  2. \(2\sim N\) 号同学依次入列,编号为 \(i\) 的同学入列方式为:老师指定编号为 \(i\) 的同学站在编号为 \(1\sim(i-1)\) 中某位同学(即之前已经入列的同学)的左边或右边;

  3. 从队列中去掉 \(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\)


思路

用一个双向链表来操作

直接创建一个结构体,其中包含:

  1. 左边、右边的同学的学号\((l, r)\)

  2. 自身的学号\((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号同学

    删除有两种做法:

    1. 将x号同学出队(展示的也是这种方法)

    2. 标记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;
} 

完结撒花(๑>؂<๑)

posted on 2025-08-28 23:43  CHNcen  阅读(11)  评论(0)    收藏  举报