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