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;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章