题目连接
- 该题是luogu试炼场的2-13:T4
题目大意
- n个数字组成的队列,多次的插入;
- 再删除其中m个元素;
- 要求输出最后的队列状态
题目分析
-
看题目第一反应是队列:
-
但因为多次的间隔插入和条空删除,应该要用链表来做,链表的思想请参考
解题思路1:双向链表
- 最开始的时候队伍里只有1号同学;
- 接下来的n-1个同学,插入到k同学的左边或者右边;
- 所以每次 i 插入的时候,更新 i 左右的元素的链接。
- 需要记录队头,因为最后要输出。
代码:
只要搞清楚前驱和后继就好
//luogu1160:队列安排
//双向链表
#include<bits/stdc++.h>
using namespace std;
int n,m,x,k,p;
int f[100005],s[100005];
int main()
{
scanf("%d",&n);
memset(f,0,sizeof(f));
memset(s,0,sizeof(s));
int t=1;//队头
for(int i=2;i<=n;i++)//i号同学进队
{
scanf("%d %d",&k,&p);
if(p==0)//k站在 i 的左边
{
f[i]=f[k]; s[i]=k;
s[f[k]]=i; f[k]=i;
if(k==t) t=i;//更新队头
}
if(p==1)//i站在 k 的左边
{
f[i]=k; s[i]=s[k];
f[s[k]]=i; s[k]=i;
}
}
scanf("%d",&m);
while(m--)
{
scanf("%d",&x);
if(f[x]==0&&s[x]==0) continue;//x已经不在队列中
if(t==x) t=s[x];//更新队头
s[f[x]]=s[x];//父亲的儿子更新
f[s[x]]=f[x];//儿子的父亲更新
s[x]=f[x]=0;//x出队
}
//输出
for(int j=t;s[j]!=t;j=s[j])
{
printf("%d ",j);
}
return 0;
}
思路2:用树来存储,中序遍历输出
- 将前驱放在左儿子;
- 将后继放在有儿子;
- 用 v 来纪录当前点是否被删除;
- 中序便利输出答案。
代码2:
- 结构体的使用,树的存储,中序遍历
//luogu1160:队列安排
//树的存储与中序遍历
#include<bits/stdc++.h>
using namespace std;
int n,m,k,p;
struct nod{int s1,s2,v;nod(){ s1=s2=v=0;} }a[100005];
//s1表示前驱,s2表示后继,v表示是否在队列内
void dfs(int x)//中序:按 左->中->右 的顺序遍历整棵树
{
if(!x) return ;//空点:边界
dfs(a[x].s1);//搜前驱(左儿子)
if(a[x].v==0) printf("%d ",x);//如果自己不为空,输出
dfs(a[x].s2);//搜后继(右儿子)
}
int main()
{
scanf("%d",&n);
//建立树
for(int i=2;i<=n;i++)
{
scanf("%d %d",&k,&p);
if(p==0)
{
if(a[k].s1)//k有前驱,更改
{
a[i].s1=a[k].s1;
a[k].s1=i;
}
else a[k].s1=i;//k没有前驱,直接赋值
}
if(p==1)
{
if(a[k].s2)
{
a[i].s2=a[k].s2;
a[k].s2=i;
}
else a[k].s2=i;
}
}
scanf("%d",&m);
while(m--)
{
scanf("%d",&k); a[k].v=1;//删除 k 点
}
dfs(1);//中序遍历,输出
return 0;
}