友塔游戏客户端笔试题
第一题:给一个9*9的矩阵,矩阵中为任意正整数值,若行列中存在连续三个以上的相同的值则消除,行列允许共用一个节点,问一次消除后,矩阵中还剩下多少个数。(感觉倒数第二难的题被放在了第一题中,笔试过程四道大题分成四个体型,意味着无法后退,这实在有点坑爹了)
例如:
此题要比原题难度高上一点,原题可以一个感染函数小小几行递归就将所有值相同的区块找出来,然后++得到所有区块;
此题感染递归就不方便了,可以非递归深度优先或非递归广度优先将当前操作的值周围与其相同的数全部先找出来,然后遍历剔除不符合横竖都至少3个相同的数.
当这一步骤完成后,将所有涉及访问过的数全部设为-1,表示已经访问过以后不需要关心这块区域,遇到的话直接返回即可.
对矩阵遍历一遍,每个数只会被访问一次,假设矩阵一共n个数,则时间复杂度O(n),但因为在剔除不符合横竖至少3个数的过程也需要
遍历,若整个矩阵全是相同数,则遍历到第一个数时回溯的代码段最终会将整个矩阵都纳入考虑,然后剔除过程复杂度O(n),总共是O(n^2)
因此假设矩阵一共有n个数,则复杂度在O(n)到O(n^2)之间.
输入: 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,我哭了。笔试问题总结一下做个记录如上,吃饭!