友塔游戏客户端笔试题

第一题:给一个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;笔试题目大意是“依据3D游戏剧本演出格式,为你的原创角色撰写故事情节”。个人研究:1.&nbsp;有看一本《游戏剧本能怎么写》的书,下图P1、P2。本书没有详细讲解游戏剧本的演出格式。本人根据P1、P2的事例中CG和动画的使用,认为CG是静态的一个画面/镜头画面。动画是动态的。“睁开眼睛”用的是动画。2.&nbsp;有拆解《原神》剧情。拆解之后个人会把《原神》中即使有动态的,比如角色在讲话时笑等表情、摊手等动作(P3)也归为CG里面,是镜头画面的一部分。而角色行走这样在画面中动作比较大的,会归为动画部分。另外有认识到原神有过场动画这么一个东西。3.个人时间有限、暂时研究到这,据此格式在作品集中写了游戏剧本【《原神》世界观下原创角色“白钰”传说任务剧本】。虽然有格式但也写的不是很规范。希望各位大佬指导一下。剧本内容如下(P4和P5):&nbsp;&nbsp;&nbsp;&nbsp;动画:旅行者和派蒙走到了琥珀前面。&nbsp;&nbsp;&nbsp;&nbsp;动画“周围场景植被发生变化,琥珀融化,树苗长成大树,白钰从地洞爬出。”派蒙:“啊!琥珀突然融化了!白钰,他自己出来了!”&nbsp;▼派蒙:“旅行者,我们就站在他的面前,但是他好像看不见我们。唔,这个秘境怎么回事!进来了又好像没进来!”&nbsp;▼旅行者:“白钰!能听见我们吗?▼&nbsp;&nbsp;&nbsp;&nbsp;或“白钰!能看见我们吗?”&nbsp;▼&nbsp;&nbsp;&nbsp;&nbsp;CG04“白钰视角:眼前没有旅行者和派蒙。转身环视一圈,观察环境,给出白钰融化的琥珀镜头、两棵大树的镜头。”白钰:“周围…嗯,怎么感觉和之前不太一样?”&nbsp;▼白钰:“唔?有人叫我名字吗?”&nbsp;▼派蒙:“他好像听到一点了,我们再试试!”&nbsp;▼动画:派蒙和旅行者站在白钰对面,疯狂呼喊,并尝试触摸白钰。手仍然变成幻影穿过。&nbsp;&nbsp;&nbsp;&nbsp;CG05“白钰眼中的画面,仍然只见风景,不见人。”白钰(挠了挠头):“应该是听错了。”&nbsp;▼白钰:“算了,前线战事吃紧,没时间耽搁了。”&nbsp;▼动画:白钰起身跑走。&nbsp;&nbsp;&nbsp;&nbsp;(慢动作;全景、侧拍、平行)白钰穿过旅行者时,旅行者身体化为幻影。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;旅行者和派蒙看着白钰跑远,在远处消失了。4.以上,问题①我对CG和动画的理解是否正确②3D游戏剧本演出格式是什么?因为种种因素也写不了非常全面,希望有大佬能告诉我基本的格式要求就行。③如果有相关的讲游戏剧本创作的书籍或媒体也可以推给我。非常感谢!!!感谢阅读。感谢解答。
点赞 评论 收藏
分享
1 27 评论
分享
牛客网
牛客企业服务