原题链接:https://www.luogu.org/problem/P1160
题目描述
一个学校里老师要将班上N个同学排成一列,同学被编号为1∼N,他采取如下的方法:
-
先将1号同学安排进队列,这时队列中只有他一个人;
-
2−N号同学依次入列,编号为i的同学入列方式为:老师指定编号为i的同学站在编号为1∼(i−1)中某位同学(即之前已经入列的同学)的左边或右边;
-
从队列中去掉M(M<N)个同学,其他同学位置顺序不变。
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。
输入输出格式
输入格式:
第1行为一个正整数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号同学已经不在队列中则忽略这一条指令。
输出格式:
1行,包含最多N个空格隔开的正整数,表示了队列从左到右所有同学的编号,行末换行且无空格。
输入输出样例
输入样例#1:
4
1 0
2 1
1 0
2
3
3
输出样例#1:
2 4 1
说明
时空限制:1000ms 125M
样例解释:
将同学2插入至同学1左边,此时队列为:2 1
将同学3插入至同学2右边,此时队列为:2 3 1
将同学4插入至同学1左边,此时队列为:2 3 4 1
将同学3从队列中移出,此时队列为:2 4 1
同学3已经不在队列中,忽略最后一条指令
最终队列:2 4 1
数据范围:
对于20%的数据,有N≤10;
对于40%的数据,有N≤1000;
对于100%的数据,有N, M≤100000。
思路:这道题插入操作多,明显用链表。可以直接用C++标准库STL的双向链表来做。
-
定义一个int类型的双向链表,以及一个数组迭代器,先把1号同学位置插入链表头部,随后把链表头部放入迭代器的开始。
-
然后遍历第2到第n号同学,如果是插入k号同学左边,直接调用insert(pos[k], i)成员函数,把i插入到k号同学左边;如果是插入k号同学右边,则新建一个迭代器iter,把k号同学位置的迭代器赋给iter,右移一位,再调用insert(iter, i)成员函数,把i插入到迭代器iter左边,即k号同学右边。
-
最后输入m位同学编号,用数组vis[i]表示第i号同学是否被移除,若未被移除,则把该同学的迭代器删除,并标为已移除,已被移除则忽略。最后把双向链表的元素依次输出来。
代码如下:
#include <iostream>
#include <cstdio>
#include <list>
using namespace std;
const int N=1e6+10;
int vis[N]={0}; //数组标记,1表示该同学已被移除,0表示未被移除
list<int> alist; //定义一个int类型的双向链表
list<int>::iterator pos[N]; //定义一个迭代器
int main() {
int n;
cin>>n; //n个人
alist.push_front(1); //将1插入到链表的头部
pos[1]=alist.begin(); //将链表的头部放入迭代器的开始
int k,p;
for(int i=2;i<=n;i++){
cin>>k>>p; //k表示同学的编号,p为1表示插入k右边,0表示插入k左边
if(!p) //插入左边
pos[i]=alist.insert(pos[k],i); //插入左边后赋给一个元素迭代器
else{ //插入右边
list<int>::iterator iter=pos[k]; //去除第k个元素迭代器的位置赋给新的迭代器
iter++; //迭代器右移一位
pos[i]=alist.insert(iter,i); //插入新迭代器右移后的左边并赋给一个元素迭代器
}
}
int m,x;
cin>>m; //要移除的数量
for(int i=1;i<=m;i++){
cin>>x; //要移除的同学编号
if(!vis[x]){ //如果未移除
vis[x]=1; //标记为已移除
alist.erase(pos[x]); //把该同学的迭代器删除,表示移除
}
}
//遍历双向链表的开始到最后一个元素
for(list<int>::iterator it=alist.begin();it!=alist.end();it++)
cout<<*it<<" ";
cout<<endl;
return 0;
}