【NOIP2004PJ】FBI树(c++,非二叉树做法)

这道题我有一个满级思路
题目描述

        我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。
        FBI树是一种二叉树,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2^N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:
(1)T的根结点为R,其类型与串S的类型相同;
(2)若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造                 R的左子树T1,由右子串S2构造R的右子树T2。
        现在给定一个长度为2^N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历序列。

输入

第一行是一个整数N(0 <= N <= 10),第二行是一个长度为2^N的“01”串。

输出

包括一行,这一行只包含一个字符串,即FBI树的后序遍历序列。

样例输入

3 10001011

样例输出

IBFBBBFIBFIIIFF


题目分析

题目让我们先求出二叉树,并求出它的后序遍历。
        后序遍历是深度优先遍历二叉树的一种方法,它的递归定义是:先后序遍历左子树,再后序遍历右子树,最后访问根。

输入n(其实n+1表示的是树的深度)

输入“01”串(表示的是叶子节点存储的数)

让我们看下图(样例)

解:由题可知,由图可知。

下面做法对于我这种不会数据结构的小菜鸡

先推二叉树叶子节点

1 对应的是 I,0 对应的是 B,那我们可以将“01”串转为“FBI”串。

10001011 转化为 IBBBIBII

cin>>n>>b;	//输入n 和 "01"串 
l=strlen(b);	//获取串的长度 
for(long long i=0;i<l;i++)	//将"01"串转为FBI串 
{
	if(b[i]=='1')	a[n+1][i]='I';	//1对应 I	 n+1为二叉树的深度 
	if(b[i]=='0')	a[n+1][i]='B';	//0对应 B
}

再向上推二叉树的父节点

左子节点右子节点父节点
III
BBB
IBF
BIF
其中一个节点为FF或B或IF
char hs(char x,char y)    //推法
{
	if(x=='I'&&y=='I')	return 'I'; //左右子节点都为 I   父节点为 I 
	if(x=='B'&&y=='B')	return 'B'; //左右子节点都为 B   父节点为 B 
	if(x=='I'&&y=='B')	return 'F';	//左子节点为 I	右子节点为 B 	父节点为 F 
	if(x=='B'&&y=='I')	return 'F';	//左子节点为 B	右子节点为 I 	父节点为 F 
	if(x=='F'||y=='F')	return 'F';	//其中一个子节点为 F 父节点为 F
}


for(long long i=n;i>=1;i--)	//开始从下往上推根节点 
{
	l/=2;	//每层的节点数为上一层节点数的一半 
	for(long long j=0;j<l;j++)
	{
		a[i][j]=hs(a[i+1][j+j],a[i+1][j+1+j]);	//二维数组模拟二叉树 
	}
}

结合一下,便可以得到FBI二叉树的模型

见下

#include<bits/stdc++.h>
using namespace std;
long long n,l;
char b[1027],a[15][1027];
char hs(char x,char y)
{
	if(x=='I'&&y=='I')	return 'I';
	if(x=='B'&&y=='B')	return 'B';
	if(x=='I'&&y=='B')	return 'F';
	if(x=='B'&&y=='I')	return 'F';
	if(x=='F'||y=='F')	return 'F';
}
int main()
{
	cin>>n>>b;
	l=strlen(b);
	for(long long i=0;i<l;i++)
	{
		if(b[i]=='1')	a[n+1][i]='I';
		if(b[i]=='0')	a[n+1][i]='B';
	}
	for(long long i=n;i>=1;i--)
	{
		l/=2;
		for(long long j=0;j<l;j++)
		{
			a[i][j]=hs(a[i+1][j+j],a[i+1][j+1+j]);
		}
	}
	for(long long i=1;i<=n+1;i++)	//输出 
	{
		for(long long j=0;j<l;j++)
		{
			cout<<a[i][j]<<" ";
		}
		cout<<endl;
		l*=2;	//从上往下节点数*2 
	}
	return 0;
}


好了,现在二叉树出来了,只剩下后序遍历了。

用数组实现的话,只用在建二叉树时记录每个节点的父节点,最好再递归查询就行了。

详细请看代码注释

void dg(long long x,long long y)	//输出父节点 
{
	if(bz[x][y]!=2)	//左子节点和右子节点没遍历完就返回 
	{
		return;
	}
	cout<<a[x][y];	//输出父节点 
	bz[xx[x][y]][yy[x][y]]++;	//父节点的父节点 标记+1 表示已被遍历 
	dg(xx[x][y],yy[x][y]);	//注意xx(i,j) yy(i,j)保存的是父节点的位置,见第36行
}




for(long long i=n;i>=1;i--)    //建二叉树 不多说
{
	l/=2;
	for(long long j=0;j<l;j++)
	{
		xx[i+1][j+j]=i,xx[i+1][j+1+j]=i;	//子节点记录父节点的位置(i,j) 
		yy[i+1][j+j]=j,yy[i+1][j+1+j]=j;
		a[i][j]=hs(a[i+1][j+j],a[i+1][j+1+j]);
	}
}
for(long long i=n+1;i>=1;i--)	//从下往上后序遍历 
{
	for(long long j=0;j<=pow(2,(i-1));j++)	//pow(2,(i-1))为第i层的节点数 
	{
		if(bz[i][j]==2)	continue;	//bz[i][j]==2 表示父节点、左子节点、右子节点都遍历了 
		cout<<a[i][j];	//没标记就输出 
		bz[xx[i][j]][yy[i][j]]++;	//父节点 标记+1 表示输出了一个子节点
		dg(xx[i][j],yy[i][j]);	//递归输出父节点,第45行就不用再输出一次父节点了 
	}
}

最后全部加起来,结果算出来,你学会了吗

见下

#include<bits/stdc++.h>
using namespace std;
long long n,l,bz[15][1027],xx[15][1027],yy[15][1027];
char b[1027],a[15][1027];
char hs(char x,char y)
{
	if(x=='F'||y=='F')	return 'F';
	if((x=='1'||x=='I')&&(y=='1'||y=='I'))	return 'I';
	if((x=='0'||x=='B')&&(y=='0'||y=='B'))	return 'B';
	if((x=='1'||x=='I')&&(y=='0'||y=='B'))	return 'F';
	if((x=='0'||x=='B')&&(y=='1'||y=='I'))	return 'F';
}
void dg(long long x,long long y)
{
	if(bz[x][y]!=2)
	{
		return;
	}
	cout<<a[x][y];
	bz[xx[x][y]][yy[x][y]]++;
	dg(xx[x][y],yy[x][y]);
}
int main()
{
	cin>>n>>b;
	l=strlen(b);
	for(long long i=0;i<l;i++)
	{
		a[n+1][i]=hs(b[i],b[i]);
	}
	for(long long i=n;i>=1;i--)
	{
		l/=2;
		for(long long j=0;j<l;j++)
		{
			xx[i+1][j+j]=i,xx[i+1][j+1+j]=i;
			yy[i+1][j+j]=j,yy[i+1][j+1+j]=j;
			a[i][j]=hs(a[i+1][j+j],a[i+1][j+1+j]);
		}
	}
	for(long long i=n+1;i>=1;i--)
	{
		for(long long j=0;j<=pow(2,(i-1));j++)
		{
			if(bz[i][j]==2)	continue;
			cout<<a[i][j];
			bz[xx[i][j]][yy[i][j]]++;
			dg(xx[i][j],yy[i][j]);
		}
	}
	return 0;
}

完结撒花!!!!!!!!!!!!!!!!!!!

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值