1. 程式人生 > >網易遊戲2016校園招聘資料探勘研究員線上筆試題和答案

網易遊戲2016校園招聘資料探勘研究員線上筆試題和答案

      剛做完網易線上筆試題,感觸最深的地方是,雖然題目形式和ACM題目相似,但是內容更偏向於實際應用。總共有四個題目,第一個題目屬於字串匹配型別,難度較低,第二個題目是模擬SQL語句的輸出,第三個題目是KNN演算法,第四個題目是貝葉斯演算法。題目偏基礎,演算法思想很容易想到,但如果平常從來沒寫過這類演算法,再加上程式碼能力不是很強的話,寫起來還是有點吃力的。下面是第一題,第三題,第四題的答案。

題目1 : 虛擬遊戲世界實體分析

時間限制:5000ms 單點時限:1000ms 記憶體限制:256MB

描述

虛擬遊戲世界裡面有很多實體,實體可能由很多子實體或者子屬性構成。由於實體之間可能有非常之多的巢狀,查詢某個實體或者屬性屬於第幾層巢狀,以便將來對虛擬世界的修改和展示是一項待解決的問題,作為未來的虛擬世界分析員,你能用程式解決這個問題嗎?

輸入

輸入資料可能由多組資料構成,每組資料由兩行構成:

第一行是對虛擬世界的描述,實體或者屬性由英文字母或者數字構成,子實體和子屬性緊接著父實體巢狀在{}中,兄弟實體或者屬性用“,”分隔。

第二行是要查詢的屬性或者實體,有且僅有一個。

注意資料輸入可能很大。

輸出

輸出為查詢的實體或者屬性的層數;如果屬性不存在,輸出-1;如果有多個結果滿足查詢,請從小到大輸出所有去重之後的結果,用”,”分隔

樣例輸入
Fruit{apple{shape,color},orange{taste,price}}
Fruit
Fruit{apple{shape,color},orange{taste,price}}
orange
Fruit{apple{shape,color},orange{color,price},color}
color
樣例輸出
1
2
2,3
程式碼:
#include <string>
#include <vector>
#include <iostream>	
#include <algorithm> 
using namespace std;

int main()
{ 
    string s;
	string query;	
    while(cin >> s >> query) {
		int i = 0;
		int j = 0;
		int h = 1;
		int begin = 0;
		vector<int> ans;
        for( i = 0; i < s.size(); i++) {
			if(s[i] == '{' || s[i] == '}' || s[i] == ',') {
				if(i - begin == query.size()) {
					for(j = 0; j < query.size(); j++) {
						if(query[j] != s[begin + j]) {
							break;
						}
					}
					if(j == query.size()) {
						ans.push_back(h);
					}
				}
				begin = i + 1;
			}
			if(s[i] == '{') {
				h++;
				
			} else if(s[i] == '}') {
				h--;
			} 
		}
		sort(ans.begin(), ans.end());  
		if(ans.size() == 0)
			cout << -1 << endl;
		else {
			cout << ans[0];
			for(int k = 1; k < ans.size(); k++) {
				if(ans[k] != ans[k-1])
					cout << "," << ans[k];
			}
			cout << endl;
		}
    }
    return 0;
}

題目3 : 遊戲玩家分類

描述

理查德•巴圖博士通過對遊戲中玩家固定的行為模式進行觀察,於1996年提出了巴圖模型,嘗試把玩家的不同行為模式進行分類。他將遊戲玩家分成了成就型、探索型、社交型和殺手型。該分類方式本質上從玩家在遊戲中的需求出發,根據具體的行為表現對其進行分類。推斷玩家所屬型別,對於遊戲使用者研究,精準營銷投放都有非常重要的意義,因此對不同玩家進行分類是一項重要研究工作。為了實現分類模型,通過收集玩家在遊戲中的不同行為資料並進行歸一化,可以得到玩家的特徵向量以及已知型別玩家的標籤,如:

副本參與次數 競技場參與次數 任務完成次數 登陸頻率 充值額度 玩家型別
0.8 0.5 0.6 0.9 0.2 A
0.4 0.8 0.1 0.2 0.1 B
0.9 0.1 0.5 0.6 0.9 C
0.5 0.2 0.1 0.3 0.0 D

(其中前五列數字為玩家的特徵向量,最後一列字母是玩家型別,有A、B、C、D四種取值)

分類問題有多種解決演算法,其中K最近鄰(k-Nearest Neighbor,KNN)分類演算法是最簡單的機器學習演算法之一,其思想是:如果一個樣本在特徵空間中的k個最相似的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別。請用該方法實現遊戲玩家分類,距離度量函式採用歐氏距離。

輸入

每個輸入資料包含一組訓練資料和一組測試資料。

第一行第一個數為KNN演算法的k(k<=10000)值,第二個數為特徵向量的長度L(L<=100),第三個數M(M>k, M<=10000)為訓練資料行數,第四個數N(N<=10000)為測試資料行數。之後是M行訓練資料和N行測試資料。每行中資料使用空格分隔。

輸出

對於每行測試資料,輸出該玩家的型別,例如“A”。如果前K個相似型別中,出現數量最多的型別有重複,則一起輸出,以ABCD升序排列,例如“AC”。

樣例輸入
3 5 16 2
0.19 0.04 0.06 0.22 0.11 A
0.28 0.42 0.38 0.39 0.44 B
0.71 0.61 0.54 0.52 0.54 C
0.98 0.82 0.92 0.98 0.97 D
0.05 0.03 0.15 0.01 0.11 A
0.33 0.29 0.33 0.47 0.27 B
0.72 0.52 0.61 0.71 0.68 C
0.78 0.86 0.91 1.0 0.76 D
0.01 0.17 0.14 0.15 0.2 A
0.44 0.36 0.32 0.32 0.35 B
0.67 0.65 0.57 0.58 0.52 C
0.87 0.92 0.8 0.83 0.77 D
0.01 0.11 0.14 0.12 0.07 A
0.33 0.43 0.43 0.45 0.38 B
0.57 0.54 0.75 0.7 0.64 C
0.9 0.94 0.83 0.96 0.77 D
0.29 0.29 0.42 0.36 0.27
0.56 0.67 0.71 0.66 0.7
樣例輸出
B
C

程式碼:

#include <string>
#include <vector>
#include <iostream>	
#include <algorithm> 
using namespace std;

typedef struct
{
	vector<double> f;
	char label;
}Elem;

typedef struct
{
	double cost;
	int idx;
}NN;

typedef struct
{
	int cnt;
	int idx;
}LabelCnt;

bool operator<(const NN &x, const NN &y)
{
    return x.cost < y.cost;
}

bool operator<(const LabelCnt &x, const LabelCnt &y)
{
    return x.cnt > y.cnt;
}

double distance(vector<double> &f1, vector<double> &f2) {
	double sum = 0.0;
	for(int i = 0; i < f1.size(); i++) {
		sum += (f1[i] - f2[i]) * (f1[i] - f2[i]);
	}
	return sum;
}
int main()
{ 
    int k,L,M,N;
    while(cin >> k >> L >> M >> N) {
		int i = 0;
		int j = 0;
		vector<Elem> trainData;
		trainData.resize(M);
		for( i = 0; i < M; i++) {
			trainData[i].f.resize(L);
			for( j = 0; j < L; j++) {
				cin >> trainData[i].f[j];
			}
			cin >> trainData[i].label;
		}
		vector<Elem> testData;
		testData.resize(N);
		for( i = 0; i < N; i++) {
			testData[i].f.resize(L);
			for( j = 0; j < L; j++) {
				cin >> testData[i].f[j];
			}
			vector<NN> nnCost;
			nnCost.resize(M);
			int t = 0;
			for( t = 0; t < M; t++) {
				nnCost[t].idx = t;
				nnCost[t].cost = distance(trainData[t].f, testData[i].f);
			}
			sort(nnCost.begin(), nnCost.end());  
			vector<LabelCnt> labels;
			labels.resize(4);
			for( t = 0; t < labels.size(); t++) {
				labels[t].cnt = 0;
				labels[t].idx = t;
			}
			for( t = 0; t < k; t++) {
				int idx = nnCost[t].idx;
				int label = trainData[idx].label - 'A';
				labels[label].cnt++;
			}
			sort(labels.begin(), labels.end()); 
			vector<int> ans;
			ans.push_back(labels[0].idx);
			for( t = 1; t < labels.size(); t++) {
				if(labels[t].cnt == labels[t-1].cnt)
					ans.push_back(labels[t].idx);
				else
					break;
			}
			for( t = 0; t < ans.size(); t++) {
				cout << (char)(ans[t]+'A');
			}
			cout << endl;
		}
    }
    return 0;
}

題目4 : 好師父推算

時間限制:5000ms 單點時限:1000ms 記憶體限制:256MB

描述

師徒系統普遍存在於各類網路遊戲中,對於遊戲促進新手留存具有重要意義,現在採集到如下資訊:

好友個數   聊天次數   是否是好師父
    1          3        1
    2          1        2

希望你用naïve bayes演算法基於“好友個數”和“聊天次數”推算某玩家是好師父的概率,以方便產品優化匹配規則。

輸入

輸入資料由多行構成,每行中的資料用“\t”分隔。第1行是1~3個用“\t”分隔的數字,表示輸出第幾個問題的答案,第2行是屬性名稱,包括fchatnum,cchatnum和remark三個屬性,分別代表好友個數、聊天次數和是否是好師父。從第3行開始為訓練資料,含義與第2行的屬性名稱相對應。好友個數和聊天次數取值都是1~10的整數,是否是好師父取值是1~2的整數,其中2表示好師父。

輸出

根據第1行輸入資料指定的編號輸出以下3個小題的答案,多個小題答案使用換行“\n”分割。

第1題:輸出好師父的先驗概率。

第2題:輸出好師父群體中好友個數取值的概率分佈,依次對應1~10的概率取值,零值也要輸出,中間用逗號分隔。

第3題:輸出給定fchatnum=9,cchatnum=9的玩家是好師父的概率。

輸出結果統一四捨五入保留小數點後3位。

完整樣例輸入下載

總計1000條資料,請在這裡下載

樣例輸入
1		2		3
fchatnum 	cchatnum	remark
1       2       1
3       3       1
1       1       1
6       9       2
3       7       2
4       6       2
4       2       2
3       8       2
1       1       1
8       4       2
……
樣例輸出
0.320
0.034,0.091,0.075,0.144,0.100,0.106,0.119,0.134,0.100,0.097
0.691

程式碼:

#include <string>
#include <vector>
#include <iostream>	
#include <algorithm> 
using namespace std;

typedef struct
{
	vector<int> f;
}Elem;

void split(string s, vector<int> &values) {
	char sep = '\t';
	int begin = 0;
	for(int i = 0; i < s.size(); i++) {
		if(s[i] == sep) {
			int num = 0;
			for(int j = begin; j < i; j++) {
				num = num * 10 + s[j] - '0';
			}
			if(num > 0)
				values.push_back(num);		
			begin = i + 1;
		}
	}
	int num = 0;
	for(int j = begin; j < s.size(); j++) {
		num = num * 10 + s[j] - '0';
	}
	if(num > 0)
		values.push_back(num);
}

double getPrior(vector<Elem> &data, int label) {
	int good = 0;
	for(int i = 0; i < data.size(); i++) {
		if(data[i].f[2] == label)
			good++;
	}
	if(good == 0)
		return 0.0;
	return 1.0*good/data.size();
}

double getPosterior(vector<Elem> &data, int idx, int k, int label) {
	int cnt = 0;
	int sum = 0;
	for(int i = 0; i < data.size(); i++) {
		if(data[i].f[2] == label) {
			sum++;
			if(data[i].f[idx] == k)
				cnt++;
		}
		
	}
	if(cnt == 0)
		return 0.0;
	return 1.0*cnt/sum;
}

int main()
{ 
	char s[100];
	vector<int> titles;
	gets(s);
	string strLine(s);
	split(strLine, titles);
	gets(s);
    vector<Elem> data;
	Elem item;
	item.f.resize(3);
    while(cin >> item.f[0] >> item.f[1] >> item.f[2]) {
		data.push_back(item);
    }
	int i = 0;
	double prior1 = getPrior(data, 1);
	double prior2 = getPrior(data, 2);
	vector<double> posterior1_0(11,0.0);
	vector<double> posterior1_1(11,0.0);
	vector<double> posterior2_0(11,0.0);
	vector<double> posterior2_1(11,0.0);
	int k;
	for(k = 1; k <= 10; k++) {
		posterior1_0[k] = getPosterior(data, 0, k, 1);
		posterior1_1[k] = getPosterior(data, 1, k, 1);
		posterior2_0[k] = getPosterior(data, 0, k, 2);
		posterior2_1[k] = getPosterior(data, 1, k, 2);
	}
	for( i = 0; i < titles.size(); i++) {
		if(titles[i] == 1) {
			printf("%.3f\n",prior2);
		} else if(titles[i] == 2) {
			for(int j = 1; j <= 9; j++) {
				printf("%.3lf,",posterior2_0[j]);
			}
			double ans = 0.0;
			ans = posterior2_0[10];
			printf("%.3lf\n",ans);
		} else if(titles[i] == 3) {
			double ans = 0.0;
			ans = prior2 * posterior2_0[9] * posterior2_1[9];
			ans /= (prior1 * posterior1_0[9] * posterior1_1[9] + prior2 * posterior2_0[9] * posterior2_1[9]);
			printf("%.3lf\n",ans);
		}
	}
    return 0;
}