luogu1160:隊列安排:雙向鏈表/樹的中序遍歷

題目連接

  • 該題是luogu試煉場的2-13:T4

題目大意

  1. n個數字組成的隊列,多次的插入;
  2. 再刪除其中m個元素;
  3. 要求輸出最後的隊列狀態

題目分析


解題思路1:雙向鏈表

  1. 最開始的時候隊伍裏只有1號同學;
  2. 接下來的n-1個同學,插入到k同學的左邊或者右邊;
  3. 所以每次 i 插入的時候,更新 i 左右的元素的鏈接。
  4. 需要記錄隊頭,因爲最後要輸出。

代碼:

只要搞清楚前驅和後繼就好

//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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章