題目很長:
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;
}