openjudge物质分解记录

题目很长:

6:物质分解记录

  • 查看
  • 提交
  • 统计
  • 提问
总时间限制: 
60000ms 
内存限制: 
131064kB
描述

对 物质分解记录 的结构进行统计分析。
例如:
给出一份 物质分解记录。
Material_1
{
Material_2
{
Material_3
                Material_4
Material_5
                {
                Material_6
                Material_7
                }
                Material_8
}
Material_9
Material_10
}
Material_11
{
Material_l3
Material_7
Material_2
{
Material_3
                Material_4
Material_5
                {
             Material_6
             Material_7
                }
                Material_8
}
Material_13
}

上述记录的含义是,Material_1分解为 Material_2、Material_9和Material_10,Material_2又分解为Material_3、Material_4、Material_5和Material_8。以此类推,大括号外书写特定物质名称,括号内表示此特定物质分解出来的子物质名称,每个子物质还可再分解。

现输入一个物质名称R,要求输出所有和物质R在记录中属于同一层次且位置在R之后的物质名称。
比如R=“Material_1” ,则应该输出“Material_11”;
比如R=“Material_9” ,则应该输出“Material_10”
如果R在代码中出现了多次,则以其第一次出现为准,即仅输出与第一次出现的R属于同一层次且位置在R之后的语句内容。
比如R=“Material_2” ,则应该输出
        Material_9
Material_10

输入
输入包含多组数据。第一行是物质分解记录的份数,仅用一个整数表示。从第二行开始,每组数据包括 物质分解记录 和 所需查找的物质R 两部分,物质分解记录样式如描述中所示,R的内容和物质分解记录之间有一行空行,下一份记录与上一个R之间有两行空行。
若输入!则表示输入结束。
为简单起见,物质分解记录中每一行的内容为“{”或者“}”或者一个物质名称,不会有其他情况(比如空行)出现。同时每行文字前不会有任何缩进。物质名称是英文字母、数字和下划线组成的字符串。
输出
对每组数据输出一行,如果R在记录中找到,则输出所有与R在同一层次且位置在R之后的物质名称,名称之间无需添加空格,紧密连接即可;否则输出No。若R是其所在层次中最后一个物质,则输出"",即输出一个空字符。
样例输入
3
Material_1
{
Material_2
{
Material_3
Material_4
Material_5
{
Material_6
Material_7
}
Material_8
}
Material_9
Material_10
}

Material_2


Material_1
{
Material_2
{
Material_3
Material_4
Material_5
{
Material_6
Material_7
}
Material_8
}
Material_9
Material_10
}
Material_11
{
Material_3
Material_7
Material_2
{
Material_3
Material_4
Material_5
{
Material_6
Material_7
}
Material_8
}
Material_13
}

Material_2


Material_1
{
Material_2
{
Material_3
Material_4
Material_5
{
Material_6
Material_7
}
Material_8
}
Material_9
Material_10
}

Material_20


!
样例输出
Material_9Material_10
Material_9Material_10
No
提示
读入数据时,需采用如下方式进行读取。
例:若要读取一行输入内容,则
cin.getline(line, lineSize, '\n');
sscanf(line, "%s", tmp);

其中line和tmp为数组指针,类型为char* ,linesize为line所指向的数组的规模,为int型。
所需读取的内容最终是存储在tmp数组中。之后如需对读取的内容进行操作,就对tmp进行操作即可,读到空行时tmp长度即为0。

采用其他方法读取可能会出现WA以及RE,TLE。


算法与实现
  1. 此题的算法较为直观:每一组测试数据中可能包含几个待分解的物质,其中每个待分解的物质就是一棵树的树根,因此每一组测试数据对应一片森林。然后在森林中按照起初建立的顺序分别查找每一棵树,找到目标结点之后,输出其亲弟弟。
  2. 森林中每一棵树的根结点储存在vector<tree_node *> forests中;树的结点包括物质名称、父结点指针与vector<tree_node *> childs三部分。
  3. 建立一棵树的过程:'{'表示它上一行的结点是父节点,在下一个'}'之前的物质都是这个父结点的儿子,之后的物质是这个父结点的兄弟(亲弟弟),因此需要找到这个父结点的父结点。这里用栈(fathers)保存这些父结点,栈顶总是当前的父结点;遇'{'其上一行结点压栈,遇'}'弹栈。
  4. 搜索结点用先根遍历的递归框架。
  5. 注意树和森林的销毁,即内存释放;测试数据中的括号不一定都是匹配的,所以还要注意fathers栈的清空。

代码清单
#include <iostream>
using namespace std;

#include <stack>
#include <vector>
#include <string>
#include <string.h>

#define MAXLEN 20

typedef struct _tree_node
{
	string mat_name;
	struct _tree_node *father;
	vector<struct _tree_node *> childs;
}tree_node;

//查找的要求是对树的先根遍历(递归)
void search_preOrder(tree_node *root, string target, bool *result_found, tree_node **result)	//前两个参数是输入,后两个参数是输出
{
	int i;
	int ret;

	if(*result_found) return;	//如果已经取得查询结果,直接返回
	else
	{
		ret = strcmp(root->mat_name.c_str(), target.c_str());
		if (ret==0/* && *result_found==false*/)
		{
			*result=root;
			*result_found=true;
		}
		else
		{
			for (i=0; i<root->childs.size(); ++i)
			{
				search_preOrder(root->childs[i], target, result_found, result);	//依次对儿子进行递归查找
			}
		}
	}
}

void delete_tree(tree_node *root)
{
	int i;
	for (i=0; i<root->childs.size(); ++i)
	{
		delete_tree(root->childs[i]);
	}
	delete root;
	root=NULL;
}


int main()
{
	freopen("D:\\in.txt", "r", stdin);
	freopen("D:\\out.txt", "w", stdout);

	int n, i, j, k, ret;
	char temp[MAXLEN];
	int temp_len;
	tree_node *root, *prev, *current;

	stack<tree_node *> fathers;
	vector<tree_node *> forests;

	string target;
	bool result_found;
	tree_node *result;

	cin>>n;
	cin.get();	//取得数字后面的换行符
	for (i=0; i<n; ++i)
	{
		//开始建立树和森林
		while(1)
		{
			cin.getline(temp, MAXLEN, '\n');
			temp_len=strlen(temp);

			if(temp_len == 0) break;	//取到空行意味着当前测试样例结束
			else
			{
				if(fathers.empty())	//说明是一棵新的树
				{
					root=new tree_node;
					root->mat_name=temp;
					root->father=NULL;

					fathers.push(root);	//树根入栈
					cin.getline(temp, MAXLEN, '\n');	//读取根结点下一行的'{'

					forests.push_back(root);	//最后别忘了把这课新建的树放在森林里
				}
				else
				{
					if(temp[0]!='{' && temp[0]!='}')	//扫描到一种物质
					{
						current = new tree_node;	//新建当前物质的结点
						current->mat_name=temp;

						current->father=fathers.top();	//当前的栈顶结点是当前结点的父结点,建立向上的连接

						current->father->childs.push_back(current);	//建立向下的连接

						prev=current;	//把当前结点暂存起来,遇到下一行是'{'说明当前结点有子孙,要进栈
					}
					else if(temp[0] == '{') fathers.push(prev);	//上一行的结点是一个父结点
					else fathers.pop();
				}
			}
		}

		//准备查找
		result_found=false;
		result=NULL;

		cin.getline(temp, MAXLEN, '\n');	//取得要查询的物质
		target=temp;

		//在森林中查询
		for (j=0; j<forests.size(); ++j)
		{
			search_preOrder(forests[j], target, &result_found, &result);
			if(result_found) break;	//找到第一个出现的地方
		}

		if (!result_found)	cout<<"No"<<endl;
		else
		{
			current=result->father;

			for (j=0; j<current->childs.size(); ++j)
			{
				ret=strcmp(current->childs[j]->mat_name.c_str(), target.c_str());
				if(ret==0) break;
			}

			if(j+1==current->childs.size())	cout<<"";	//若是最后一个,输出一个空字符
			else
			{
				for(k=j+1; k<current->childs.size(); ++k)	cout<<current->childs[k]->mat_name;
			}

			cout<<endl;
		}

		cin.getline(temp, MAXLEN, '\n');	//一组结束时的两个空行
		cin.getline(temp, MAXLEN, '\n');

		//释放森林中每一棵树的内存
		for (j=0; j<forests.size(); ++j)	delete_tree(forests[j]);

		//清空父结点栈
		while(!fathers.empty()) fathers.pop();

		//清空森林
		forests.clear();
	}

	return 0;
}




你可能感兴趣的:(树,森林,STL,OpenJudge,物质分解记录)