题目:洛谷:P1160 队列安排。
大概思路
初始化
首先有一个数字 1 ,我们需要让接下来的数字站在它的左边或右边,这样就会改变它的链接关系,我们需要先建立一个链接关系,才能让它在后期时可以操作操作。因为 1 的前驱是 0 ,所以先让他和 0 连上,所以:next[0]=1,pre[1]=0;
:将 0 的后继设为 1 , 1 的前驱设为 0 。最后要让 1 和他后面的数连上,这样做目的既可以有一个明确的结尾,也可以让后面输出的时候有停止的条件。总之:链接他的前后关系可以让它和其它之后的操作变得一样。
所以:第一部分程序:
#include<bits/stdc++.h>//头文件
using namespace std;//命名空间
int next_[100001];//后继
int pre_[100001];//前驱
//因为next和pre已经在Dev-c++里有定义,所以加上下划线可以避免暴0
int n;//n个人
int m;//m次删除的操作
int k;//放在k的左边或右边
int p;//左边或右边
int x;//删除的数
int main(){
cin>>n;//一共有n个人
next_[0]=1;//0的后继是1
pre_[1]=0;//1的前驱是0
next_[1]=-1;//建立末尾标记好理解
输入与链接关系
因为 1 已经初始化了所以从 2 开始输入:
for(int i=2;i<=n;i++){
cin>>k>>p;
当 p 为 1 时,将 i 放在 k 的右边。
所以 k,i 的关系是 k->i
。
我们设想原来的关系是 k->x
,而现在加入了 i ,从而将关系变成了 k->i->x
。其中 x 是在 i 加入之前 k 的后继
所以 x 在 i 加入之前为 next[k]
。
最后设定他们三之间的关系:
if(p==1){//k->x ==> k->i->x
x=next_[k];
next_[k]=i;//k->i
next_[i]=x;//i->x
pre_[i]=k;//k<-i
pre_[x]=i;//i<-x
}
当 p 为 0 时,将 i 放在 k 的左边。
所以 k,i 的关系是 i<-k
。
我们设想原来的关系是 x<-k
,而现在加入了 i ,从而将关系变成了 x<-i<-k
。其中 x 是在 i 加入之前 k 的前驱
所以 x 在 i 加入之前为 pre[k]
。
最后设定他们三之间的关系:
if(p==0){//x<-k ==> x<-i<-k
x=pre_[k];
pre_[k]=i;//i<-k
pre_[i]=x;//x<-i
next_[x]=i;//x->i
next_[i]=k;//i->k
}
所以:第二部分程序:
for(int i=2;i<=n;i++){//1已经给了所以从2开始放
cin>>k>>p;//在k的左或右
if(p==1){//k->x ==> k->i->x
x=next_[k];//x在k的右边
next_[k]=i;//k->i
next_[i]=x;//i->x
pre_[i]=k;//k<-i
pre_[x]=i;//i<-x
}
if(p==0){//x<-k ==> x<-i<-k
x=pre_[k];//x在k的左边
pre_[k]=i;//i<-k
pre_[i]=x;//x<-i
next_[x]=i;//x->i
next_[i]=k;//i->k
}
}
删除部分
相比前两个部分,这个部分较为简单。
首先有 m 个要删除的数,所以建立一个 for 循环:for(int i=1;i<=m;i++)
表示一共有 m 个要删除的数。
删除并不是真正意义上的删除,而是断开他的前后链接。
简单说成:
- 将它前面的数和它后面的数连上,也就是将他的前驱的后继设为他的后继:
next[pre[x]]=next[x];
; - 将它后面的数和它前面的数连上,也就是将他的后继的前驱设为他的前驱:
pre[next[x]]=pre[x];
;
最后将此数彻底与数列断开:将他的前后设为 -1 或其他的题目没出现的数,这样可以避免重复断开。
程序:
cin>>m;//断开的次数
for(int i=1;i<=m;i++){//断开m次
cin>>x;//要断开的数
if(pre_[x]==-1&&next_[x]==-1)continue;//已经断开过避免重复断开
next_[pre_[x]]=next_[x];//将前面和后面链接
pre_[next_[x]]=pre_[x];//将后面和前面链接
pre_[x]=-1;//断绝x的前面的连接
next_[x]=-1;//断绝x的后面的连接
}
输出部分
第一个数是开头 0 (下标)的后继:next[0]
。
最后一个数是 -1 之前定义的,因为前面删除的数已经和前后彻底断开了链接,所以不用考虑 -1 串了,如果实在不放心可以将删除的 -1 改成 -2 或其他不可能出现的数。
所以程序思路是:
从第一个数开始不停的链接后面的数并输出,直到到达 -1 (末尾)。
输出部分程序:
int t=next_[0];//第一个数
while(t!=-1){//边界
cout<<t<<" ";//输出
t=next_[t];//连接的下一个数
}
总体程序
#include<bits/stdc++.h>//头文件
using namespace std;//命名空间
int next_[100001];//后继
int pre_[100001];//前驱
//因为next和pre已经在Dev-c++里有定义,所以加上下划线可以避免暴0
int n;//n个人
int m;//m次删除的操作
int k;//放在k的左边或右边
int p;//左边或右边
int x;//删除的数
int main(){
cin>>n;//一共有n个人
next_[0]=1;//0的后继是1
pre_[1]=0;//1的前驱是0
next_[1]=-1;//建立末尾标记好理解 //初始化
for(int i=2;i<=n;i++){//1已经给了所以从2开始放
cin>>k>>p;//在k的左或右
if(p==1){//k->x ==> k->i->x
x=next_[k];//x在k的右边
next_[k]=i;//k->i
next_[i]=x;//i->x
pre_[i]=k;//k<-i
pre_[x]=i;//i<-x
}
if(p==0){//x<-k ==> x<-i<-k
x=pre_[k];//x在k的左边
pre_[k]=i;//i<-k
pre_[i]=x;//x<-i
next_[x]=i;//x->i
next_[i]=k;//i->k
}
} //输入与链接
cin>>m;//断开的次数
for(int i=1;i<=m;i++){//断开m次
cin>>x;//要断开的数
if(pre_[x]==-1&&next_[x]==-1)continue;//已经断开过避免重复断开
next_[pre_[x]]=next_[x];//将前面和后面链接
pre_[next_[x]]=pre_[x];//将后面和前面链接
pre_[x]=-1;//断绝x的前面的连接
next_[x]=-1;//断绝x的后面的连接
} //删除数字
int t=next_[0];//第一个数
while(t!=-1){//边界
cout<<t<<" ";//输出
t=next_[t];//连接的下一个数
} //输出部分
return 0;//结束程序
}
\color{red}感\color{gold}谢\color{orange}观\color{green}看\color{blue}!