網易互娛2017實習生招聘在線筆試(二)

題目2 : 源代碼編譯

時間限制:10000ms
單點時限:1000ms
內存限制:256MB

描述

在網易遊戲的日常工作中,C++ 是一門常用的語言。面對衆多的 C++ 代碼,等待源文件編譯的漫長時間是個令人糟心的時刻,一直以來大家對此怨聲載道。終於有一天,大家找到了你,一位優秀的程序員,請你來幫忙分析一下編譯速度的瓶頸。

經過一番調查和研究,你發現一些源代碼之間是有依賴關係的。例如,某個源文件 a.cpp 編譯鏈接生成了動態鏈接庫 a.dll,而 b.cpp 編譯鏈接生成的 b.dll 依賴於 a.dll。這個時候,必須等待 a.dll 生成之後才能生成 b.dll。爲了表達簡單,我們這個時候稱 b.cpp 依賴於 a.cpp。

網易遊戲內部使用了一個分佈式並行的編譯平臺,可以同時編譯多個不互相依賴的文件,大大提高了源代碼的編譯速度。然而當某些依賴鏈很長的時候,這個編譯平臺也無能爲力,只能按照依賴順序一個一個完成編譯,從而造成了很長的編譯時間。

爲了驗證這個想法,你決定着手通過代碼分析這些文件之間的編譯順序。已知這些文件的文件名,以及這些文件所依賴的其他文件,你需要編寫一個程序,輸出一個可行的編譯所有源文件的編譯順序。如果有多種可行的序列,請輸出所有文件名序列中字典序最小的那一個(序列 (a1, a2, ..., an) 字典序小於序列 (b1, b2, ..., bn),當且僅當存在某個 i ,使得 ai 的字典序小於 bi,並且對於任意 j < i ,都有 aj = bj)。

輸入

輸入包含多組測試數據。

輸入的第一行包含一個整數 T(T ≤ 100),表示輸入中一共包含有 T 組測試數據。

每組測試數據第一行是一個整數 N(N ≤ 1000),表示一共有 N 個源代碼文件。隨後一共有 N 行數據,其中第 i(0 ≤ i < N) 行數據包含序號爲 i 的源代碼文件的依賴信息。每一行開頭是一個字符串,表示這一個文件的文件名,隨後一個整數 m(0 ≤ mN),表示編譯這個源文件之前需要先編譯 m 個依賴文件。之後是 m 個整數 j0 ... jm-1,表示這 m 個依賴文件的序號(0 ≤ j < N) 。所有的文件名僅由小寫字母、數字或“.”組成,並且不會超過 10 個字符。保證 n 個源代碼文件的文件名互不相同。

輸出

對於每一組輸入,按照編譯先後順序輸出一組可行的編譯順序,一行一個文件名。如果有多種可行的序列,請輸出所有文件名序列中字典序最小的那一個。如果不存在可行的編譯順序,輸出一行 ERROR。每組測試數據末尾輸出一個空行。

樣例輸入
3
2
a.cpp 0
b.cpp 1 0
2
cb 0
c 0
2
a.cpp 1 1
b.cpp 1 0
樣例輸出
a.cpp
b.cpp

c
cb

ERROR

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
#include <set>
#include <cassert>
#include <time.h>
#include <queue>
#include <map>
#include <stack>
#include <bitset>
#include <string>

using namespace std;

const int maxn = 1000 + 5;
int n;
vector<int> G[maxn];
int inDegree[maxn];
map<string, int> id;
string cppName[maxn];

void input()
{
	//init
	memset(inDegree, 0, sizeof(inDegree)); //inDegree初始化賦值0
	for (int i = 0; i < maxn; ++i) {
		G[i].clear();
	}
	id.clear();
	scanf("%d", &n);
	for (int u = 0; u < n; ++u) { //N 個源代碼文件
		cin >> cppName[u];   //文件名
		id[cppName[u]] = u;  //序號
		int m;
		scanf("%d", &m);     //m個依賴文件
		while (m--) {
			int v;
			scanf("%d", &v);
			G[v].push_back(u);   //u依賴於v,所以在v中存儲u !!!!
			++inDegree[u];       //圖的入度
		}
	}
}

//自定義結構,運算符重載
struct QueNode
{
	int id;
	QueNode(int _id)
		: id(_id) { }
	//友元函數不是類的一部分,但又需要頻繁地訪問類的數據成員
	//友元的作用是提高了程序的運行效率(即減少了類型檢查和安全性檢查等都需要時間開銷),它破壞了類的封裝性和隱藏性,使得非成員函數可以訪問類的私有成員。
	friend bool operator < (const QueNode & n1, const QueNode & n2)
	{
		return cppName[n1.id] > cppName[n2.id];
	}
};


void solve()
{
	priority_queue<QueNode> que; //優先隊列會根據比較運算符,自動排序
	for (int i = 0; i < n; ++i) if (!inDegree[i]) {
		que.push(QueNode(i)); //將入度爲0的文件序號存儲,按文件名排序
	}

	vector<int> ans;
	while (!que.empty()) {
		int u = que.top().id; que.pop();
		ans.push_back(u);   //結果,即入度爲0
		for (int i = 0; i < G[u].size(); ++i) { //依次依賴於u的文件的入度-1 !
			int v = G[u][i];
			if (--inDegree[v] == 0) que.push(QueNode(v));
		}
	}

	if (ans.size() != n) {
		puts("ERROR");
	} else {
		for (int i = 0; i < ans.size(); ++i) {
			printf("%s\n", cppName[ans[i]].c_str());
		}
	}
}

int main()
{
	int T; cin >> T;
	while (T--) { //T組測試數據
		input();  //輸入
		solve();  //處理與輸出
		puts("");
	}
}


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