刚做完网易在线笔试题,感触最深的地方是,虽然题目形式和ACM题目相似,但是内容更偏向于实际应用。总共有四个题目,第一个题目属于字符串匹配类型,难度较低,第二个题目是模拟SQL语句的输出,第三个题目是KNN算法,第四个题目是贝叶斯算法。题目偏基础,算法思想很容易想到,但如果平常从来没写过这类算法,再加上代码能力不是很强的话,写起来还是有点吃力的。下面是第一题,第三题,第四题的答案。
虚拟游戏世界里面有很多实体,实体可能由很多子实体或者子属性构成。由于实体之间可能有非常之多的嵌套,查询某个实体或者属性属于第几层嵌套,以便将来对虚拟世界的修改和展示是一项待解决的问题,作为未来的虚拟世界分析员,你能用程序解决这个问题吗?
输入数据可能由多组数据构成,每组数据由两行构成:
第一行是对虚拟世界的描述,实体或者属性由英文字母或者数字构成,子实体和子属性紧接着父实体嵌套在{}中,兄弟实体或者属性用“,”分隔。
第二行是要查询的属性或者实体,有且仅有一个。
注意数据输入可能很大。
输出为查询的实体或者属性的层数;如果属性不存在,输出-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; }
理查德•巴图博士通过对游戏中玩家固定的行为模式进行观察,于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; }
师徒系统普遍存在于各类网络游戏中,对于游戏促进新手留存具有重要意义,现在采集到如下信息:
好友个数 聊天次数 是否是好师父 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; }