网易游戏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.80.50.60.90.2A
0.40.80.10.20.1B
0.90.10.50.60.9C
0.50.20.10.30.0D

(其中前五列数字为玩家的特征向量,最后一列字母是玩家类型,有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;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值