網易遊戲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;
}