能力提升綜合題單Part 8.3.1 二叉樹&Part 8.3.2 樹的直徑

1.P1087 FBI樹

遞歸求解,判斷是否爲’0’串或者’1’串即可

#include<bits/stdc++.h>
using namespace std;
string a;
int n;
void chuan(string x){
	if(x.length()>0){
	int len=x.length();
	chuan(x.substr(0,len/2));
	chuan(x.substr(len/2,len-1));
	if (x == string(x.length(), '0')) cout<<'B';
    else if (x == string(x.length(), '1')) cout<<'I';
	else{
		cout<<'F';
	}
	}
}
int main(){
	cin>>n>>a;
	chuan(a);
	return 0;
}

2.P1030 求先序排列

給出了中序和後序排列,求先序排列
由這兩種輸出方式的性質可知,根在後序的最後輸出,所以我們找到根爲分界線,不斷遞歸爲兩個子樹進行求解

#include<bits/stdc++.h>
using namespace std;
string a,b;
void dfs(string x,string y){
	if(x.length()>0){
	int len=y.length();
	cout<<y[len-1];
	char ch=y[len-1];
	int t=x.find(ch);
	dfs(x.substr(0,t),y.substr(0,t));
	dfs(x.substr(t+1,len-1),y.substr(t,len-t-1));}
}
int main(){
	cin>>a>>b;
	dfs(a,b);
	return 0;
}

3.P1305 新二叉樹

給一棵樹,輸出先序遍歷,直接遞歸即可

#include<bits/stdc++.h>
using namespace std;
struct node{
	char lc,rc;
}t[200];
char t1,tt;
void xianxu(char x){
	if(x=='*') return;
	cout<<x;
	xianxu(t[x].lc);
	xianxu(t[x].rc);
}
int main(){
	int n;
	cin>>n;
	cin>>t1;
	cin>>t[t1].lc;
	cin>>t[t1].rc;
	for(int i=2;i<=n;i++){
		cin>>tt;
		cin>>t[tt].lc;
		cin>>t[tt].rc;
	}
	xianxu(t1);
	return 0;
}

4.P1229 遍歷問題

給出先序和後序遍歷,求這棵樹最多有多少種不同中序遍歷的情況
中序+x 可以求出這棵樹
轉化一下題意就是求有多少個子樹只有一個兒子

#include<bits/stdc++.h>
using namespace std;
string a,b;
int main(){
	cin>>a;
	cin>>b;
	int ans=0;
	int n=a.length();
	for(int i=0;i<n;i++){
		for(int j=1;j<n;j++){
			if(a[i]==b[j]&&a[i+1]==b[j-1]){
				ans++;
			}
		}
	}
	printf("%d",1<<ans);
}

5.P5018 對稱二叉樹

遍歷每一個節點,遞歸判斷子樹是否對稱,並更新最大值

#include<bits/stdc++.h>
using namespace std;
#define int long long
struct node{
	int l,r,w;
}t[1000005];
int n;
bool same(int x,int y){
	if(x==-1&&y==-1)return true;
	if(x==-1||y==-1)return false;
	if(t[x].w!=t[y].w)return false;
	return same(t[x].l,t[y].r)&&same(t[x].r,t[y].l);
}
int ct(int x){
	return x==-1?0:ct(t[x].l)+ct(t[x].r)+1;
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%lld",&t[i].w);
	}
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",&t[i].l,&t[i].r);
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(same(i,i))ans=max(ans,ct(i));
	}
	cout<<ans;
	return 0;
}

6.P5597 【XR-4】復讀

咕咕咕待補

7.P2195 HXY造公園

操作1,輸出該點所在的樹的直徑
操作2,將u,v所在的樹連接,並且要求連接後的樹的直徑儘量小
怎麼實現操作2?選兩個直徑的中點進行合併

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
int f[maxn],c[maxn],vis[maxn],n,m,q,g[maxn],d[maxn],len;
vector<int>e[maxn];
int find(int x){
	if(x==f[x])return x;
	return f[x]=find(f[x]);
}
void dfs(int x,int fa){
	int m1=-1,m2=-1;
	for(int i=0;i<e[x].size();++i){
		int y=e[x][i];
		if(y==fa)continue;
		dfs(y,x);
		int tmp=d[y]+1;
		d[x]=max(d[x],tmp);
		if(tmp>m1)m2=m1,m1=tmp;
		else if(tmp>m2)m2=tmp;
	}
	g[x]=max(max(0,m1+m2),max(m1,m2));
	len=max(len,g[x]);
}
void calc(int x)
{
	len=0;
	dfs(x,0);
	c[x]=len;
}
int main(){
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		e[u].push_back(v);
		e[v].push_back(u);
		f[find(u)]=find(v);
	}
	for(int i=1;i<=n;i++){
		if(f[i]!=i||vis[i])continue;
		calc(i);
		vis[i]=1;
	}
	int tt,u,v;
	while(q--){
		scanf("%d",&tt);
		if(tt==1){
			scanf("%d",&u);
			printf("%d\n",c[find(u)]);
			continue;
		}
		scanf("%d%d",&u,&v);
		int x=find(u);
		int y=find(v);
		if(x==y)continue;
		int t=((c[x]+1)/2)+((c[y]+1)/2)+1;
		t=max(t,max(c[x],c[y]));
		f[find(x)]=find(y);
		c[find(x)]=t;
	}
	return 0;
}

8.P3629 [APIO2010]巡邏

有兩種情形,k=1的時候只需要找到直徑,連接兩端即可
k=2時,如果連接的邊經過了第一條路徑,就要再走一遍,所以把第一條已經連好的路上權值取負
x-y-(-y)=x
由於dfs求直徑不能處理負數,所以用dp求,也是這個題的精髓所在,需要用dfs求直徑來記錄路徑,用dp來處理負權

#include<bits/stdc++.h>
using namespace std;
const int maxm=2e5+5;
int head[maxm],dis[maxm],n,k,cnt=1,pos,st,ed;
int f[maxm],vis[maxm];
struct edge{
	int v,w,nex;
}e[maxm];
inline void add(int u,int v){
	e[++cnt].v=v;
	e[cnt].w=1;
	e[cnt].nex=head[u];
	head[u]=cnt;
}
void dfs(int u,int fa){
	if(dis[u]>dis[pos]||pos==st)pos=u;
	f[u]=fa;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa)continue;
		dis[v]=dis[u]+e[i].w;
		dfs(v,u);
	}
	return;
}
void change(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa)continue;
		if(vis[v]){
			e[i].w=-1;
			e[i^1].w=-1;
		}
		change(v,u);
	}
	return;
}
int l2;
void dp(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa)continue;
		dp(v,u);
		l2=max(dis[v]+dis[u]+e[i].w,l2);
		dis[u]=max(dis[u],dis[v]+e[i].w);
	}
	return;
}
int main(){
	cin>>n>>k;
	int u,v;
	for(int i=1;i<=n-1;i++){
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	st=1;
	pos=1;
	dis[1]=0;
	dfs(st,0);
	st=pos;
	dis[st]=0;
	dfs(st,0);
	ed=pos;
	int l1=dis[ed];
	int r=ed;
	while(r){
		vis[r]=1;
		r=f[r];
	}
	change(st,0);
	memset(dis,0,sizeof(dis));
	dp(st,0);
	if(k==1)l2=1;
	int ans=n*2-l1-l2;
	cout<<ans;
	return 0;
}

9.P5536 【XR-3】核心城市

在樹中間選一些點,使其他點到這些點的最大距離最小
先求直徑,再求中點,中點一定要被選中,然後以中點爲根建樹,進行一次dfs,統計每個點向下延伸的最大深度,對子節點進行排序,顯然第k+1個節點+1即爲答案(因爲記錄的是最大延伸,需要加上自己與核心城市的距離1)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n,k,head[maxn],f[maxn],st,ed,pos,dis[maxn],cnt,vis[maxn],len[maxn];
struct edge{
	int v,w,nex;
}e[maxn];
vector<int>ve;
inline void add(int u,int v){
	e[++cnt].v=v;
	e[cnt].w=1;
	e[cnt].nex=head[u];
	head[u]=cnt;
}
void dfs(int u,int fa){
	if(dis[u]>dis[pos]||pos==st)pos=u;
	f[u]=fa;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa)continue;
		dis[v]=dis[u]+1;
		dfs(v,u);
	}
	return;
}
void dfs3(int sn,int fa){
	len[sn]=0;
	for(int i=head[sn];i;i=e[i].nex){
		int v=e[i].v;
		if(v!=fa){
			dfs3(v,sn);
			len[sn]=max(len[sn],len[v]+1);
		}
	}
}
bool cmp(int x,int y){
	return x>y;
}
int main(){
	cin>>n>>k;
	for(int i=1;i<=n-1;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	st=1,pos=1,dis[1]=0;
	dfs(st,0);
	st=pos,dis[st]=0;
	dfs(st,0);
	ed=pos;
	int l1=dis[ed];
	int st2=ed;
	for(int i=1;i<=(l1+1)/2;i++){
		st2=f[st2];
	}
	dfs3(st2,0);
	sort(len+1,len+1+n,cmp);
	cout<<len[k+1]+1;
	return 0;
}

10.P1099 樹網的核

P2491 [SDOI2011]消防
U89620 樹網的核加強版
這兩題是數據加強板(三倍經驗)
前提是用最優解做的
直接上數據最強的一個版本的代碼

大體的思路是這樣的,先用dfs求出直徑,然後從端點進行遍歷,選擇每一段小於等於s的路徑,求出兩端點到這段路徑的最小距離,
然後求出每個直徑上的點到這個點的最小距離,跟ans進行比較

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int n,s,head[maxn],f[maxn],st,ed,pos,dis[maxn],cnt,vis[maxn],len[maxn];
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
struct edge{
	int v,w,nex;
}e[maxn];
inline void add(int u,int v,int w){
	e[++cnt].v=v;
	e[cnt].w=w;
	e[cnt].nex=head[u];
	head[u]=cnt;
}
void dfs(int u,int fa){
	if(dis[u]>dis[pos]||pos==st)pos=u;
	f[u]=fa;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa||vis[v]==1)continue;
		dis[v]=dis[u]+e[i].w;
		dfs(v,u);
	}
	return;
}
void dfs2(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa||vis[v]==1)continue;
		dis[v]=dis[u]+e[i].w;
		dfs(v,u);
	}
	return;
}
int main(){
	cin>>n>>s;
	int u,v,w;
	for(int i=1;i<=n-1;i++){
		u=read(),v=read(),w=read();
		add(u,v,w);
		add(v,u,w);
	}
	st=1,pos=1,dis[1]=0;
	dfs(st,0);
	st=pos,dis[st]=0;
	dfs(st,0);
	ed=pos;
	int ans=0x3f3f3f3f;
	int tmp=ed;
	for(int i=ed;i;i=f[i]){
		while(f[tmp]&&dis[i]-dis[f[tmp]]<=s){
			tmp=f[tmp];
		}
		ans=min(ans,max(dis[ed]-dis[i],dis[tmp]));
	}
	for(int i=ed;i;i=f[i]){
		vis[i]=1;
		dis[i]=0;
	}
//	cout<<ed<<endl;
//	for(int i=1;i<=n;i++){
//		printf("%d %d  vis:%d\n",i,f[i],vis[i]);
//	}
	for(int i=ed;i;i=f[i]){
		dfs2(i,0);
	}
	
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			ans=max(ans,dis[i]);
		}
	}
	cout<<ans;
	return 0;
}

11.P4408 [NOI2003]逃學的小孩

求非直徑上的點到直徑的兩端點的最大值

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=4e5+5;
int dis2[maxn],dis3[maxn];
int n,m,head[maxn],f[maxn],st,ed,pos,dis[maxn],cnt,vis[maxn],len[maxn];
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
struct edge{
	int v,w,nex;
}e[maxn];
inline void add(int u,int v,int w){
	e[++cnt].v=v;
	e[cnt].w=w;
	e[cnt].nex=head[u];
	head[u]=cnt;
}
void dfs(int u,int fa){
	if(dis[u]>dis[pos]||pos==st)pos=u;
	f[u]=fa;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa)continue;
		dis[v]=dis[u]+e[i].w;
		dfs(v,u);
	}
	return;
}
void dfs2(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa||vis[v]==1)continue;
		dis2[v]=dis2[u]+e[i].w;
		dfs2(v,u);
	}
	return;
}
void dfs3(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa||vis[v]==1)continue;
		dis3[v]=dis3[u]+e[i].w;
		dfs3(v,u);
	}
	return;
}
signed main(){
	cin>>n>>m;
	int u,v,w;
	for(int i=1;i<=m;i++){
		u=read(),v=read(),w=read();
		add(u,v,w);
		add(v,u,w);
	}
	st=1,pos=1,dis[1]=0;
	dfs(st,0);
	st=pos,dis[st]=0;
	dfs(st,0);
	ed=pos;
	int ans=dis[ed];
	dfs2(st,0);
	dfs3(ed,0);
//	for(int i=1;i<=n;i++){
//		printf("i=%d dis2 %d dis3 %d\n",i,dis2[i],dis3[i]);
//	}
	int tmp=0;
	for(int i=1;i<=n;i++){
		int d=min(dis2[i],dis3[i]);
		tmp=max(tmp,d);
	}
	cout<<ans+tmp;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章