友塔游戏客户端笔试题

第一题:给一个9*9的矩阵,矩阵中为任意正整数值,若行列中存在连续三个以上的相同的值则消除,行列允许共用一个节点,问一次消除后,矩阵中还剩下多少个数。(感觉倒数第二难的题被放在了第一题中,笔试过程四道大题分成四个体型,意味着无法后退,这实在有点坑爹了)
例如:
 输入:
    5 2 2 4 8 9 5 3 5
    3 2 2 5 7 5 2 1 2
    1 2 2 2 1 1 3 2 1
    2 2 5 1 2 5 8 1 8
    6 1 2 3 7 9 5 6 5
    4 3 2 1 5 8 4 5 4
    8 4 5 5 4 4 5 8 7
    2 1 3 2 1 5 8 7 9
    9 2 4 9 8 7 5 3 1

    输出:81-8=73

(以下这个解法划掉,太傻了。被提醒后,发现好的解法应该是横竖遍历一遍矩阵,只判断自己是否属于可消除的点,用一个布尔矩阵记录当前点是否被消除过,这样时间复杂度O(n)解了,且思路和编码都很简单
我的想法:和剑指offer还是哪个地方的题很相似,题目是说一个01矩阵中找出所有相连的1的区块有多少个,这个突然找不到出处了.
此题要比原题难度高上一点,原题可以一个感染函数小小几行递归就将所有值相同的区块找出来,然后++得到所有区块;
此题感染递归就不方便了,可以非递归深度优先或非递归广度优先将当前操作的值周围与其相同的数全部先找出来,然后遍历剔除不符合横竖都至少3个相同的数.
当这一步骤完成后,将所有涉及访问过的数全部设为-1,表示已经访问过以后不需要关心这块区域,遇到的话直接返回即可.
对矩阵遍历一遍,每个数只会被访问一次,假设矩阵一共n个数,则时间复杂度O(n),但因为在剔除不符合横竖至少3个数的过程也需要
遍历,若整个矩阵全是相同数,则遍历到第一个数时回溯的代码段最终会将整个矩阵都纳入考虑,然后剔除过程复杂度O(n),总共是O(n^2)
因此假设矩阵一共有n个数,则复杂度在O(n)到O(n^2)之间.
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <unordered_set>
using namespace std;

string crStr(int row, int col)
{
	return to_string(row) + '-' + to_string(col);
}

bool isValid(unordered_set<string>& hashset, pair<int, int>&p)
{
	int curRow = p.first, curCol = p.second;
	//竖
	int topRow = curRow, downRow = curRow;
	while (hashset.find(crStr(topRow - 1, curCol)) != hashset.end() && downRow - topRow + 1 <= 2)--topRow;
	while (hashset.find(crStr(downRow + 1, curCol)) != hashset.end() && downRow - topRow + 1 <= 2)++downRow;
	if (downRow - topRow + 1 > 2)
		return true;

	//横
	int leftCol = curCol, rightCol = curCol;
	while (hashset.find(crStr(curRow, leftCol - 1)) != hashset.end() && rightCol - leftCol + 1 <= 2)--leftCol;
	while (hashset.find(crStr(curRow, rightCol + 1)) != hashset.end() && rightCol - leftCol + 1 <= 2)++rightCol;
	if (rightCol - leftCol + 1 > 2)
		return true;

	return false;
}

void myAdd(vector<vector<int>>& input, queue<pair<int, int>>& equalque, unordered_set<string> &hashset, int row, int col)
{
	string tmp = crStr(row, col);
	if (hashset.find(tmp) == hashset.end())
	{
		hashset.insert(tmp);
		equalque.push(pair<int, int>(row, col));
	}
}

int getNum(vector<vector<int>>& input)
{
	if (input.empty())return 0;
	int rows = input.size();
	int cols = input[0].size();
	int res = rows * cols;
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			int anchor = input[i][j];
			if (anchor == -1)continue;
			//找出所有值相同的块,暂且标志为0,并加入哈希表与数组中为剔除工作做准备
			queue<pair<int, int>> equalque;
			vector<pair<int, int>>equalVec;
			unordered_set<string> hashset;
			myAdd(input, equalque, hashset, i, j);
			while (!equalque.empty())
			{
				int curRow = equalque.front().first, curCol = equalque.front().second;
				input[curRow][curCol] = 0;
				equalVec.push_back(equalque.front());
				equalque.pop();
				if (curRow + 1 < rows && input[curRow + 1][curCol] == anchor)myAdd(input, equalque, hashset, curRow + 1, curCol);
				if (curRow - 1 >= 0 && input[curRow - 1][curCol] == anchor)myAdd(input, equalque, hashset, curRow - 1, curCol);
				if (curCol + 1 < cols &&  input[curRow][curCol + 1] == anchor)myAdd(input, equalque, hashset, curRow, curCol + 1);
				if (curCol - 1 >= 0 && input[curRow][curCol - 1] == anchor)myAdd(input, equalque, hashset, curRow, curCol - 1);
			}
			res -= equalVec.size();
			//剔除横竖没有连续超过3个相同的
			for (auto p : equalVec)
				if (!isValid(hashset, p))
					res++;

			//剔除完毕,将区块值全部更新为-1
			for (auto p : equalVec)input[p.first][p.second] = -1;
		}
	}
	return res;
}

int main()
{
	/*
	9*9矩阵中均为正数,行列中值连续出现超过3个相同的就消除掉,问一次消除后剩下多少个数
	输入:
	5 2 2 4 8 9 5 3 5
	3 2 2 5 7 5 2 1 2
	1 2 2 2 1 1 3 2 1
	2 2 5 1 2 5 8 1 8
	6 1 2 3 7 9 5 6 5
	4 3 2 1 5 8 4 5 4
	8 4 5 5 4 4 5 8 7
	2 1 3 2 1 5 8 7 9
	9 2 4 9 8 7 5 3 1

	输出:81-8=73

	我的想法:和剑指offer还是哪个地方的题很相似,题目是说一个01矩阵中找出所有相连的1的区块有多少个,这个突然找不到出处了.
	此题要比原题难度高上一点,原题可以一个感染函数小小几行递归就将所有值相同的区块找出来,然后++得到所有区块;
	此题感染递归就不方便了,可以非递归深度优先或非递归广度优先将当前操作的值周围与其相同的数全部先找出来,然后遍历剔除不符合横竖都至少3个相同的数.
	当这一步骤完成后,将所有涉及访问过的数全部设为-1,表示已经访问过以后不需要关心这块区域,遇到的话直接返回即可.
	对矩阵遍历一遍,每个数只会被访问一次,假设矩阵一共n个数,则时间复杂度O(n),但因为在剔除不符合横竖至少3个数的过程也需要
	遍历,若整个矩阵全是相同数,则遍历到第一个数时回溯的代码段最终会将整个矩阵都纳入考虑,然后剔除过程复杂度O(n),总共是O(n^2)
	因此假设矩阵一共有n个数,则复杂度在O(n)到O(n^2)之间.
	*/
	int n = 9;
	vector<vector<int>> input(n);
	for (int i = 0; i < n; i++)
	{
		input[i].resize(n);
		for (int j = 0; j < n; j++)
		{
			int tmp = 0;
			scanf("%d", &tmp);
			input[i][j] = tmp;
		}
	}
	printf("%d", getNum(input));
}



第二题:进度条问题(最简单),题意是:有一个进度条,一开始时预测n帧能走完,进度条要从0走到1,但是在其中某些帧中情况突变,发现需要b帧才能走完,请输出每帧进度条的值,其中输出时要求四舍五入且保留两位小数:
#include <algorithm>
#include <string>
#include <vector>
using namespace std;

bool mintomax(pair<int, int>& p1, pair<int, int>&p2)
{
	return p1.first < p2.first;
}

int main()
{
	//比如测试用例:初始假设n帧,并且接下来有k条记录,这k条(a,b)记录分别为第a帧时情况改变,此时预测进度条结束时的帧为第b帧
	/*输入:
	10 3
	6  12
	4  8
	8  10
	输出:
	0.10
	0.20
	0.30
	0.40
	0.55
	0.70
	0.75
	0.80
	0.90
	1.00
	*/
	int n,k = 0;
	scanf("%d", &n); scanf("%d", &k);

	vector<pair<int,int>> vec(k);
	for (int i = 0; i < k; i++)
	{
		pair<int, int> tmp;
		scanf("%d", &tmp.first); scanf("%d", &tmp.second);
		vec[i] = tmp;
	}
	sort(vec.begin(),vec.end(), mintomax);

	float progress = 0;
	float speed = 1.0f / n;
	int curFrame = 0,vecInd=0;

	while (progress < 1)
	{
		if (vecInd<k&&vec[vecInd].first == curFrame)
			speed = (1 - progress) / (vec[vecInd++].second - curFrame);
		progress += speed;
		++curFrame;

		float outputProgress = progress;
		outputProgress = round(outputProgress * 100) / 100;
		printf("%.2f\n", outputProgress);
	}
}


第三题:陆地与水相接值的标注问题(难度最大,题目没完全看懂并且也没截图,事后要想比较困难),题目先给了一个5*5的岛屿图,岛屿被分为25块方块,正***是水面,水面形状不平整,有的方块全是水,有的方块全是陆地,有的方块是水与陆地相交,而根据水面与陆地的相交是凹还是凸的形状又可分为几种类型,若水面与陆地相交为凸且角度为??则标志为5,若角度为??则标志为6,若……则标注为7,若……则标注为8;若水面与陆地相交且为凹,若角度为……则标志为9,……10,……11,……12;其中水面与陆地的上、右、下、左边界分别标注为1、2、3、4,陆地标志位-2,纯水标志位0,现在给定一个由0与-1组成的矩阵,要求将矩阵中为 -1的值更新为符合上述标准的范围在0-12中的整数。这题自己感觉摸不清深浅,跳了。

第四题:一维数组消除问题,若连续两个数以上相同则可以消除(次简单)。比如1 2 2 3 3 2,可以先让第二位和第三位的2-2消除,再让第四第五位的3-3消除,或者是先让第四第五位的3-3消除后,再让第二第三第六位的2消除,消除得分为消除的数量的平方,本例中最大分数为2^2+3^2=13,现在给定一个数组,问最大消除分数为多少(好像是这个意思,现在回想起来感觉又感觉有点模糊了,题意也可能是说玩家移动相邻两个数字后若左右数字相同就消除,感觉均可递归解。在递归解法中,直接相连消除或是交换前后两位数后若存在相邻相同则消除只是判断过滤条件稍稍不同,没太大差别。)
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

bool getEqual(vector<int>&vec, int index, int&left, int&right)
{
	if (index < 0 || index >= vec.size()||vec.size()==1)return false;
	if (index == 0)
	{
		if (vec[1] == vec[0])
		{
			left = 0, right = 1;
			while (right + 1 < vec.size() && vec[right + 1] == vec[right])right++;
			return true;
		}
		return false;
	}
	if (index == vec.size() - 1)
	{
		if (vec[index] == vec[index - 1]) {
			right = index, left = index - 1;
			while (left - 1 >= 0 && vec[left] == vec[left - 1])left--;
			return true;
		}
		return false;
	}
	left = index, right = index;
	if (vec[index] != vec[index + 1] && vec[index] != vec[index - 1])return false;
	while (left - 1 >= 0 && vec[left - 1] == vec[left])left--;
	while (right + 1 < vec.size() && vec[right + 1] == vec[right])right++;
	return true;
}

int _solution(vector<int>vec, int index)
{
	if (index < 0 || index >= vec.size())return 0;
	int notTake =INT_MIN;
	int take = INT_MIN;
	int left, right;
	if (getEqual(vec, index, left, right))
	{
		vector<int> tmp;
		for (int i = 0; i < left; i++)tmp.push_back(vec[i]);
		for (int i = right + 1; i < vec.size(); i++)tmp.push_back(vec[i]);
		take=_solution(tmp, 0)+(right-left+1)*(right - left + 1);
		notTake = _solution(vec, right + 1);
	}
	else
	{
		notTake= _solution(vec, index + 1);
	}
	int max=(notTake < take ? take : notTake);
	if (max==INT_MIN)max = 0;
	return max;
}

int solution(vector<int>& vec)
{
	return _solution(vec, 0);
}

int main()
{
	/*测试用例:
	6
	1 2 2 3 3 2
	输出:13
	*/
	int n = 0;
	scanf("%d", &n);
	vector<int> input(n);
	for (int i = 0; i < n; i++)
	{
		int tmp = 0;
		scanf("%d", &tmp);
		input[i] = tmp;
	}
	cout << solution(input);
}


昨天上午的笔试,友塔hr前几天打电话来说笔试时间从15-18号,自己任选时间作答,感觉还挺好,但18号一上手,这难度我这菜鸡哭了啊。
笔试时间一共两小时.
第一题怼了一小时,当时没法看后续题目,不敢放,结果一小时也没做出来,ac=0。实际上我事后重新写一遍程序,改bug处理各种点也绝对超出了一小时,这难度比我水平高多了。(思路错了,如果是横竖遍历一遍用布尔记录是否重复算过就简单了,难度也并非不合理吧,自己的锅。)
第二题简单,但我自己的锅因为紧张,先是完了保留两位小数的格式化怎么输出,无奈将数字转成字符串后找小数点截取到后两位,然后输出发现还是没法100%,发现忘记处理四舍五入,这时候就更紧张了,当时就在想,如果一个数时0.4444444445,要四舍五入怎么做,直接懵逼。事后还是群友提醒下想起来先保留小数再四舍五入,ac=0.1。
第三题题目都看了半小时,发现惹不起跑了,剩下十分钟看最后一题,ac=0.
第四题感觉难度不大,递归一下就解了,但是时间不足没写完,ac=0. 题目考试时感觉可dp,但是事后发现好像没什么重复点,既然是多次消除,我的解法是数组本身也在变,递归并没有出现额外重复计算,不需要优化。

总结,两小时的结果得分0.1,我哭了。笔试问题总结一下做个记录如上,吃饭!

#友塔游戏##笔试题目#
全部评论
感觉好像比我的难😂
点赞 回复 分享
发布于 2019-08-19 19:46
是真的难啊,所以过多少才会有面试机会啊
点赞 回复 分享
发布于 2019-08-29 17:15
兄弟,我第三题和你的一样,看题目看了半个多小时,完全没思路🤣
点赞 回复 分享
发布于 2019-09-19 21:36
请问点了“下一题”就不能后退回来做前面的题目了吗?答题的过程中可以退出来查手册吗?没有笔试过,通知我晚上笔试有点惶恐😂
点赞 回复 分享
发布于 2020-03-03 12:41
刚做完,感觉好难啊,我只a了0.36,哭了😥
点赞 回复 分享
发布于 2020-03-25 20:59
我刚做完ac83,100,100,16大概是我这几个月来笔试第二顺畅的😡不知道后面面试咋样,我还没offer真的慌啊
点赞 回复 分享
发布于 2020-04-02 21:02
第四题是区间dp?
点赞 回复 分享
发布于 2020-04-13 20:39

相关推荐

北京教育中厂的成都分部&nbsp;&nbsp;11.5时长有28分钟一面二面合并了面试官人挺好的,也不是特别急,答的不是很好的问题面试官直接跳过了顺序不是很统一&nbsp;只写记得的部分1.问了值类型和引用类型的区别2.&nbsp;问了ugui的组件有哪些ugui有哪些优化方案答用打图集来减少内存的消耗Unity有哪些优化方案答用对象池以及少用全局变量3.引用类型储存在堆上是怎么储存的&nbsp;好像是这个记不清了&nbsp;没答出来&nbsp;问了两道算法4一个三角形,一个三角形有三个顶点,然后有一个点如何判断另一个点在这个三角形的内部还是外部?自己答出来的是从内部找一个顶点连线,实际上答案是可以用面积来求以及用向量夹角来求。5还有一个快排怎么排的?以及怎么优化快排答:怎么排的想起来了,但是没有说优化方法问了一下,快排怎么实现的?答的凑合6.了解哪些设计模式答:了只了解单例模式,然后说了说单例模式怎么用7.面向对象的三个特征&nbsp;和五个原则答出来了特征没答出来原则8协程是怎么实现的?&nbsp;底层原理是什么?答用迭代器9.问了一下字典如何储存值以及字典的存东西的原理没答上来&nbsp;&nbsp;&nbsp;看了一下,发现好像是直接add&nbsp;remove就行(不知道是不是)&nbsp;麻了10.最后是一道场景题,问,工作时如果遇见了上边发配的任务已经完成不了了,时间特别紧急,要截止,你该怎么做?答&nbsp;:先自己憋一会儿,憋1到2个小时,实在想不出来找组长问一问怎么办,11.问是不是自学的游戏?&nbsp;答说是自己学的12.问了问背包系统的制作需要用到哪些ugui组件没答好,光说了说用图片组件,还有一个能让,图片排列规矩的组件。。。。。这我当时还自己做过,然而实在记不清了都是三四个月之前了反问环节:问了问,公司是做什么项目的?回答&nbsp;公司是做学龄前儿童的绘本小游戏&nbsp;&nbsp;呃,问实习生需要干什么&nbsp;&nbsp;得到回答,实习生需要在绘本中一些益智小游戏的整体的游戏逻辑搭建总结:基础有一些但是还是不太熟练&nbsp;得仔细看看那个unity面经&nbsp;当然算法和数据结构也得常常复习&nbsp;差点快排怎么排没答上来。。。。汗流浃背了当时
跳进黄河洗不清女士:怎么了。是好未来嘛
查看15道真题和解析
点赞 评论 收藏
分享
评论
1
27
分享
牛客网
牛客企业服务