自学内容网 自学内容网

P1160 队列安排题解

题目

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

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

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

  3. 从队列中去掉M个同学,其他同学位置顺序不变。

在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。

输入输出格式

输入格式

第一行一个整数N,表示了有N个同学。 

第2∼N行,第i行包含两个整数k,p,其中k为小于i的正整数,p为0或者1。若p为0,则表示将i号同学插入到k号同学的左边,p为1则表示插入到右边。

第N+1行为一个整数M,表示去掉的同学数目。

接下来M行,每行一个正整数x,表示将x号同学从队列中移去,如果x号同学已经不在队列中则忽略这一条指令。

输出格式

一行,包含最多N个空格隔开的整数,表示了队列从左到右所有同学的编号。

输入输出样例

输入样例

4
1 0
2 1
1 0
2
3
3

输出样例

2 4 1

补充知识

链表的使用,可以完成以下的操作,实现一个数据结构,维护一张表(最初只有一个元素1)。支持以下的操作,其中x,y都是int范围内的正整数,且都不一样。

(1)ins_back(x,y):将元素y插入到x后面

(2)ins_front(x,y):将元素y插入到x前面

(3)ask_back(x):询问x后面的元素

(4)ask_front(x):询问x前面的元素

(5)del(x):从表中删除元素x,不改变其他元素的先后顺序

struct node{
int pre,nxt;//分别记录前驱和后继结点在数组s中的下标
int key;
node(int _key=0,int _pre=0,int _nxt=0){
pre=_pre;
nxt=_nxt;
key=_key;
}
};
node s[100005];
//一个池,以后想要新建一个结点,就从s数组里面拿出一个位置给新结点
//也可以使用指针new,delete实现
int tot=0;//记录s数组目前使用了多少个位置,那么下一个可用的位置就是s[tot+1]
int find(int x){//查找x的结点编号,需要遍历整个链表
    int now=1;
while(now&&s[now].key!=x){
now=s[now].nxt;
return now;
} 
} 
void ins_back(int x,int y){//y插在x后面 
int now=find(x);
s[++tot]=node(y,now,s[now].nxt);
s[s[now].nxt].pre=tot;
s[now].nxt=tot;
}
void ins_front(int x,int y){//y插在x前面 
int now=find(x);
s[++tot]=node(y,s[now].pre,now);
s[s[now].pre].nxt=tot;
s[now].pre=tot;
}
int ask_back(int x){
int now=find(x);
return s[s[now].nxt].key;
}
int ask_front(int x){
int now=find(x);
return s[s[now].pre].key;
}
void del(int x){
int now=find(x);
int le=s[now].pre,rt=s[now].nxt;
s[le].nxt=rt;
s[rt].pre=le;
}

当然STL库也提供了链表的相关操作,需要使用list的头文件。支持以下的常用方法。

(1)list<int> a:定义一个int类型的链表a

(2)int arr[5]={1,2,3};lsit<int> a(arr,arr+3):从数组arr中的前三个元素作为链表a的初始值

(3)a.size():返回链表的结点数量

(4)list<int>::iterator it:定义一个名为it的迭代器(指针)

(5)a.begin();a.end():链表开始和末尾的迭代器指针

(6)it++;it--:迭代器指向前一个和后一个元素

(7)a.push_front(x);a.push_back():在链表开头或者末尾插入元素x

(8)a.insert(it,x):在迭代器it的前插入元素x

(9)a.pop_front();a.pop_back():删除链表开头或者末尾

(10)a.erase(it):删除迭代器it所在的元素

(11)for(it=a.begin();it!=a.end();it++):遍历链表

解析

此题目利用一个双向链表维护这个队伍,每个同学记录自己左边和右边的同学。这样各种操作都可以O\left ( 1 \right )的复杂度完成。可以使用上面的链表模板,但是需要稍微修改一下插入函数和删除函数。使用数组index定位某位同学的结点编号,在插入和删除时直接找到这位同学的结点编号,在插入时还要记录下这名同学的结点编号。这样就不需要每次都要遍历整个链表了。

#include<iostream>
using namespace std;
struct node{
int pre,nxt,key;
node(int _key=0,int _pre=0,int _nxt=0){
pre=_pre;
nxt=_nxt;
key=_key;
}
};
node s[100005];
int n,m,tot=0,index[100005]={0};//记录每个位置的结点编号
void ins_back(int x,int y){
int now=index[x];//查找索引
s[++tot]=node(y,now,s[now].nxt);
s[s[now].nxt].pre=tot;
s[now].nxt=tot;
index[y]=tot;//记录索引
}
void ins_front(int x,int y){
int now=index[x];
s[++tot]=node(y,s[now].pre,now);
s[s[now].pre].nxt=tot;
s[now].pre=tot;
index[y]=tot;
}
void del(int x){
int now=index[x];
int le=s[now].pre,rt=s[now].nxt;
s[le].nxt=rt;
s[rt].pre=le;
index[x]=0;
}
int main(){
int x,k,p,now;
cin>>n;
s[0]=node();//令0恒为最左边的结点
ins_back(0,1);
for(int i=2;i<=n;i++){
cin>>k>>p;
p ? ins_back(k,i):ins_front(k,i);
}
cin>>m;
for(int i=1;i<=m;i++){
cin>>x;
if(index[x]){
del(x);
}
}
now=s[0].nxt;
while(now){
cout<<s[now].key<<" ";
now=s[now].nxt;
}
return 0;
}

原文地址:https://blog.csdn.net/m0_72674633/article/details/136377912

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!