• 【数据结构/递归】P1087 FBI树


    题目描述
    我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。

    FBI树是一种二叉树,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2^N
    的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:

    T的根结点为R,其类型与串S的类型相同;
    若串S的长度大于1,将串S从中间分开,分为等长的左右子串S_1
    和S_2 ;由左子串S_1构造R的左子树T_1,由右子串S_2构造R的右子树T_2。
    现在给定一个长度为2^N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历序列。

    输入格式
    第一行是一个整数N(0≤N≤10),
    第二行是一个长度为2^N的“01”串。

    输出格式
    一个字符串,即FBI树的后序遍历序列。

    输入输出样例
    输入 #1
    3
    10001011
    输出 #1
    IBFBBBFIBFIIIFF
    说明/提示
    对于40%的数据,N≤2;

    对于全部的数据,N≤10。

    noip2004普及组第3题



    这道题是二叉树的题中比较有意思的一道,难度不大。从FBI这个充满杀气的名字里就能看出它有多么的霸(搞)气(笑)。
    楼上把各种建树、遍历的方法都说得很详细了。这里我主要讲一讲结构体、“枚举类型(enum)”和string的子串的使用。代码可能稍微冗长,但是思路非常清晰,非常适合初学者。
    拿样例数据来说,图片如下:

    这里我采用了从上到下从左向右编号,假设输入的第一个参数为n,可以发现:


    1.编号在闭区间

    [[2 ^ n,2 ^ {n+1}-1] ]

    的结点都是叶结点,没有左右子结点。

    2.编号在半开半闭区间

    [[0,2^n) ]

    的结点是非子结点,第i个结点(i在上述范围内)的左、右孩子编号为

    [2i,2i+1. ]

    3.没有只有一个孩子的结点。


    这是所有满二叉树的共同性质。这两个性质非常重要,一会要用到。
    我的思路是,首先构建一个结构体,用来存储树上每个结点。这里,我的上面说的“枚举类型(enum)”就派上用场了。


    枚举类型(又叫枚举变量)是一种数据类型,在C++中对应关键字enum。它用来表示一些具有同样性质的变量的集合体。和数组略有不同,枚举类型更像是一种符号。它的声明方式为:

    enum agent{Vanguard,Sniper,Guard,Caster,Defender,Medic,Specialist,Supporter};
    

    有人能看出来这是什么吗,有惊喜的

    其中列举了几个变量,它们具有各自的值。一般来说,第一个变量的值是0,后面的递增。你也可以指定每个变量的值:

    enum agent{Vanguard=1,Sniper,Guard,Caster,Defender,Medic,Specialist,Supporter};
    

    这样第一个元素的值是1,后面的递增。

    声明一个枚举类型的变量:

    enum agent{Vanguard,Sniper,Guard,Caster,Defender,Medic,Specialist,Supporter};
    int main()
    {
    	agent Amiya=Caster;
    	cout<<Amiya;
    }
    

    输出为3。因为对于上述枚举类型,第一个元素是0,后面每个递增(是前一个的数值+1),可以得出Caster的值是3。

    enum还有很多其他的用法。关于更多细节可以查看->某不知名官网关于enum的介绍

    其实是我懒得


    对于这个题,每个结点就是有F、B、I三种状态,用枚举类型再好不过了。不过要注意,直接输出枚举变量会输出数字,需要用switch转为字符。

    enum type{F=1,B,I};
    

    然后构建结构体:

    struct node
    {
    	type t//枚举类型;
    	int left//左儿子编号;
    	int right//右儿子编号;
        //对于叶结点,它们的值都是0。这也是在递归遍历时停止的条件。
    	void node_init(string s)//用字符串初始化每个结点的属性
    	{
    		bool have1=false,have0=false;
    		for(int i=0;i<s.length();i++)//按照题意
    		{
    			if(s[i]=='0') have0=true;
    			if(s[i]=='1') have1=true;
    		}
    		if(have0==true&&have1==false) t=B;
    		else if(have0==false&&have1==true) t=I;
    		else t=F;//上面三行,type型枚举变量被赋值。
    	}
    }a[2050];//题目中限定n<=10,加上根节点的一层就是11层,共有2048个结点。
    

    我们还需要一个函数来初始化各个结点的儿子情况,赋值规则上面已经提到:

    int n;
    void init()
    {
    	for(int i=1;i<(int)pow(2,n);i++)
    	{
    		a[i].left=i*2;
    		a[i].right=i*2+1;
    	}
    	for(int i=(int)pow(2,n);i<=pow(2,n+1);i++)
    	{
    		a[i].left=a[i].right=0;
    	}
    }
    

    然后是递归地使用一个字符串和一个结点的编号来设定每个结点的FBI值。递归到叶节点时直接返回,表示当前递归道路搜索完毕:

    void make_tree(string s,int code)
    {
    	if(code!=0)//如果一个结点是叶结点,那么它的孩子编号都是0,初始化时就能体现这一点。
    	{
    		a[code].node_init(s);//用当前字符串初始化
    		make_tree(s.substr(0,s.length()/2),a[code].left);//截取左半部分
    		make_tree(s.substr(s.length()/2,s.length()/2),a[code].right);//截取右半部分
    	}
    }
    

    这里的substr函数是string的特有函数。用来截取一个string对象里的某一段。用法:

    substr(<开始截取的字符串开头下标>,<截取字符串的长度>);

    例如:

    string s="012345";
    cout<<string.substr(0,3);//从0开始,截取长度为3的字符串
    

    输出:012


    它可以轻松的分割一个串,比char字符串好多了!


    最后是愉快的后序遍历。就像刚才强调的那样,要使用switch来对应地输出字符,而不是直接输出变量:

    void back(int code)
    {
    	if(code!=0)
    	{
    		back(a[code].left);
    		back(a[code].right);
    		switch(a[code].t)
    		{
    			case F://也可以是case 1
    				cout<<"F";
    				break;
    			case B:
    				cout<<"B";
    				break;
    			case I:
    				cout<<"I";
    				break;
    		}
    	}
    }
    

    最后,愉快地写主程序,完美地AC!

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>//pow(幂)函数,调用数学库
    using namespace std;
    enum type{F=1,B,I};
    struct node
    {
    	type t;
    	int left;
    	int right;
    	void node_init(string s)
    	{
    		bool have1=false,have0=false;
    		for(int i=0;i<s.length();i++)
    		{
    			if(s[i]=='0') have0=true;
    			if(s[i]=='1') have1=true;
    		}
    		if(have0==true&&have1==false) t=B;
    		else if(have0==false&&have1==true) t=I;
    		else t=F;
    	}
    }a[2050];
    int n;
    void init()
    {
    	for(int i=1;i<(int)pow(2,n);i++)
    	{
    		a[i].left=i*2;
    		a[i].right=i*2+1;
    	}
    	for(int i=(int)pow(2,n);i<=pow(2,n+1);i++)
    	{
    		a[i].left=a[i].right=0;
    	}
    }
    void make_tree(string s,int code)
    {
    	if(code!=0)
    	{
    		a[code].node_init(s);
    		make_tree(s.substr(0,s.length()/2),a[code].left);
    		make_tree(s.substr(s.length()/2,s.length()/2),a[code].right);
    	}
    }
    void back(int code)
    {
    	if(code!=0)
    	{
    		back(a[code].left);
    		back(a[code].right);
    		switch(a[code].t)
    		{
    			case F:
    				cout<<"F";
    				break;
    			case B:
    				cout<<"B";
    				break;
    			case I:
    				cout<<"I";
    				break;
    		}
    	}
    }
    string m;
    int main()
    {
    	cin>>n;
    	cin>>m;
    	init();
    	make_tree(m,1);
    	back(1);
    	return 0;
    }
    
    

    本人写这篇题解弄到晚上10:40,看在这么辛苦的份上,点个赞再走呗~(✪ω✪)

  • 相关阅读:
    idou老师教你学Istio 08: 调用链埋点是否真的“零修改”?
    idou老师教你学Istio 07: 如何用istio实现请求超时管理
    idou老师教你学Istio06: 如何用istio实现流量迁移
    Strusts2笔记8--文件的上传和下载
    Strusts2笔记7--国际化
    Strusts2笔记6--拦截器
    Strusts2笔记5--数据验证
    Strusts2笔记4--类型转换器
    Struts2笔记3--获取ServletAPI和OGNL与值栈
    Struts2笔记2--动态方法调用和Action接收请求方式
  • 原文地址:https://www.cnblogs.com/jiangyuechen/p/12748649.html
Copyright © 2020-2023  润新知