題目連接
- 該題是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;
}