P1160 队列安排

这次的数据类型但凡过了CSP的IOer都知道。
P1160 队列安排

题目大意:

一个闲着没事干的老师,让 n ( 1 ≤ n ≤ 100000 ) n(1\le n\le 100000) n(1n100000)个同学排成一列,他们被依次编号为 1 ∼ n 1\sim n 1n,老师先让一个 1 1 1号同学进入队列(此时队列中只有 1 1 1号同学),然后再让 2 ∼ n 2\sim n 2n号同学依次进入队列,老师会让 i i i号同学站在 1 ∼ ( i − 1 ) 1\sim(i-1) 1(i1)号同学的左边或是右边,待所有同学进入队列后,老师会让 m ( 1 ≤ m ≤ n ≤ 100000 ) m(1\le m\le n\le 100000) m(1mn100000)位同学退出队列。
老师想知道,等他的这一顿猛如虎的操作结束后,他想知道目前队列中的学生从左到右的编号依次是多少。

解题思路&代码实现:

这道题要求我们使用能随心所欲的插入、删除元素,普通的数组显然我发做到这一点,于是,就把考CSP时压箱底的数据类型——链表搬出来了。
链表,这玩意不同于其他数组,其他类型的数组除了加入、去除元素的方式有些不同外,其他的都差不多,毕竟他们的存储方式都是记录自己本身的值,而链表呢?他记的是前驱( p r e pre pre)和后继( n x t nxt nxt),这么说把,它几下了它同桌的名字,而且记得牢牢的,但不知道自己叫啥,它查询自己的值,就得用以下两种方法:

  • 问自己的前驱:“你的后继叫啥名儿?”
a[i]==a[a[i].pre].nxt;
  • 问自己的后继:“你的前驱叫啥名儿?”
a[i]==a[a[i].nxt].pre;

麻烦是麻烦了点,但它有一个好处在于,可以方便的插入、删除元素,这不正好是我们需要的吗?

首先,我们得搞清楚怎么在链表里插入、删除元素。

  • 插入在左边

假设原先队列里是 [ 1 , 2 ] [1,2] [1,2],我们要在 2 2 2的左边插入元素3,我们的操作分为如下步骤:
1.将 1 1 1的后继设为 3 3 3
2.将 2 2 2的前驱设为 3 3 3
3.将 3 3 3的前驱设为 1 1 1
4.将 3 3 3的后继设为 2 2 2

struct node{
	int pre,nxt;
};
node a[5]={node(0,1),node(0,2),node(1,4),node(0,0);node(2,0)};
//链表数据为:[1,2];
int main(){
	int k,i;
	k=2;
	i=3
	int q;
	q=a[k].pre;
	a[q].nxt=i;
	a[k].pre=i;
	a[i].pre=q;
	a[i].nxt=k;
	//现在链表数据为[1,3,2]
}
  • 插入在右边

假设原先队列里是 [ 1 , 2 ] [1,2] [1,2],我们要在 1 1 1的右边插入元素3,我们的操作分为如下步骤:
1.将 2 2 2的前驱设为 3 3 3
2.将 1 1 1的后继设为 3 3 3
3.将 3 3 3的后继设为 2 2 2
4.将 3 3 3的前驱设为 1 1 1

struct node{
	int pre,nxt;
};
node a[5]={node(0,1),node(0,2),node(1,4),node(0,0);node(2,0)};
//链表数据为:[1,2];
int main(){
	int k,i;
	k=1;
	i=3
	int q;
	q=a[k].nxt;
	a[q].pre=i;
	a[k].nxt=i;
	a[i].nxt=q;
	a[i].pre=k;
	//现在链表数据为[1,3,2]
}

现在,我们知道了怎么插入,那么,删除呢?

删除的流程:
还是 [ 1 , 3 , 2 ] [1,3,2] [1,3,2]的链表,我们要删除 3 3 3,我们的操作分为以下步骤:
1.将 1 1 1的后继改成 2 2 2;(将 3 3 3的前驱的后继改成 3 3 3的后继)
2.将 2 2 2的前驱改成 1 1 1。(将 3 3 3的后继的前驱改成 3 3 3的前驱)

struct node{
	int pre,nxt;
};
node a[5]={node(0,1),node(0,3),node(3,4),node(1,2);node(2,0)};
//链表数据为:[1,3,2];
int main(){
	k=3;
	a[a[k].pre].nxt=a[k].nxt;
	a[a[k].nxt].pre=a[k].pre;
	//现在链表数据为[1,2]
}

同时,我们还要用数组 v v v来记录每一位同学是否在队列里,防止误删,还要把 1 1 1号同学的前驱、后继指向不存在的同学 0 、 n + 1 0、n+1 0n+1
好的,直接开码~~

#include<bits/stdc++.h>
using namespace std;
struct node{//结构体
	int pre,nxt;
};
node a[100005];
bool v[100005];//v[i]表示编号为i的学生是否已出队,防止重复删除导致WA
int n,m,k,p;//n,m不会的请重新阅读本文,k表示操作位置,p表示操作类型
int main(){
	cin>>n;//输入同学总数
	int i;
	a[1].pre=0;
	a[1].nxt=n+1;//把1号同学的前驱、后继指向不存在的同学0、n+1。
	for (i=2;i<=n;i++){
		cin>>k>>p;
		if (p==0){
			int q;
			q=a[k].pre;
			a[q].nxt=i;
			a[k].pre=i;
			a[i].pre=q;
			a[i].nxt=k;//在k的左边插入i
		}
		else{
			int q;
			q=a[k].nxt;
			a[q].pre=i;
			a[k].nxt=i;
			a[i].nxt=q;
			a[i].pre=k;//在k的右边插入i
		}
	}
	cin>>m;//输入要退出队列的同学个数
	for (i=1;i<=m;i++){
		cin>>k;
		if (v[k]){
			continue;
		} 
		a[a[k].pre].nxt=a[k].nxt;
		a[a[k].nxt].pre=a[k].pre;
		v[k]=1;
	}
	int fir=a[0].nxt;
	while (fir!=n+1){
		cout<<fir<<' ';
		fir=a[fir].nxt; 
	}//输出
}

AC
直接AC,完结撒花!
祝你AC!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值