ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

网易2019秋招笔试真题解析:五大高频编程题思路详解

网易2019秋招笔试真题解析:五大高频编程题思路详解 网易2019秋季校园招聘的这套编程题真题在各大刷题社区和牛客网上流传得很广很多准备校招的同学会拿它当第一套完整模拟题。我前前后后陪好几个学弟学妹复盘过这套题自己也把每道题重新写了一遍。整体感受是网易的笔试不考偏题怪题题型集中在字符串处理、数学规律、动态规划、简单模拟这几类而且每一题都藏着一个足以让通过率大幅下降的思维陷阱。这篇文章就把2019秋招真题里最有代表性的几道题拿出来从题意、暴力思路、正解推导到完整代码一步步拆开讲把我踩过的坑、调过的边界情况都交代清楚。这篇内容不追求“最优解大全”而是想还原笔试现场一道题从拿到手到AC的完整思考路径。无论你是正在准备校招的应届生还是想系统练算法的新手这套真题都值得刷而且值得刷完以后仔细复盘。1. 网易秋招笔试的真实面貌题型、难度与时间分配1.1 题量、时长与通过率网易校招在线笔试一般安排在牛客网这类平台上时长通常90到120分钟编程题一般是4道偶尔是3道或5道。2019年秋季这套真题以4道题为主覆盖了动态规划、数学、贪心、模拟四个方向。题量不算多但每一道题都不白给。从通过率来看每年笔试结果都挺残酷的。我看到的统计是大部分岗位笔试淘汰率在70%到85%之间。淘汰率高的原因往往不是题目本身难到无法下手而是三个隐性门槛第一输入输出用的是ACM模式很多在学校里只写过核心代码的同学不适应第二边界条件极其容易被忽略比如整数范围、取模、字符串长度等第三时间分配不合理在一道题上死磕导致后面题目没有时间写。1.2 难度梯度与命题风格网易的编程题有一个很明显的风格第一题通常是思维题或简单模拟送分但容易粗心第二题开始上数学规律第三题是动态规划或贪心最后一题一般是个综合应用或者压轴的思维题。这套2019年的真题也不例外。你看完会发现难题的“难”不是算法本身藏得深而是你在考场上有没有见过类似模型。比如跳石板一眼过去像BFS实际上用动态规划更好写被3整除那道题暴力拼数根本拼不完得从数位和往下推俄罗斯方块那道题表面是个模拟游戏实际上就是求一列的最小值。这种“把包装扯掉露出核心”的命题方式是网易非常喜欢的。之所以推荐这套题作为校招备考的第一套完整真题就是因为它很典型难度分布合理适合拿来诊断自己的薄弱环节。如果你能稳定在90分钟内做出3道以上再配合针对性补强笔试环节基本不会拖后腿。1.3 做题顺序建议我的习惯是拿到题目先花1分钟扫一遍四道题心里给难度排个序。这套2019年真题里俄罗斯方块和时钟都是短代码题适合先写被3整除虽然代码也短但推导规律需要点时间可以放在中间跳石板和小易的字典需要想清楚状态和贪心策略放到后面以免卡住。千万不要按题号顺序硬做尤其是第一题如果卡住了情绪很容易崩。先把能拿的分稳稳拿到再回来啃硬骨头这是所有机考的通用策略。2. 真题逐题拆解从暴力到正解以下题意为整理后的常用描述版本考试时的具体表述可能略有出入核心逻辑不变。每题我都按“题意理解、思路演变、正解推导、参考代码、易错点”的顺序来讲。2.1 跳石板动态规划加因子枚举题意小易在一条石板路上从编号为N的石板出发目标是跳到编号为M的石板。每次跳跃假设当前在编号为x的石板上他可以选择一个x的约数dd不等于1且d不等于x跳到编号为xd的石板上。问最少跳几次能从N到达M如果无法到达则输出-1。这道题在很多刷题网站上都收录了是2019网易秋招里比较典型的动态规划题目。我第一次做的时候第一反应是BFS因为“最短步数”听起来就和BFS匹配。但试了一下发现状态就是N到M之间所有整数每个点扩展出的边取决去它的约数数量最坏情况下每个数的约数个数不超过两倍根号级别BFS本身也能做。不过BFS写起来要维护队列和访问数组稍微绕一点。动态规划的思路更直接定义dp[i]表示从N跳到i的最少步数初始dp[N] 0其余设为无穷大。从N开始往后扫描对于每个i如果dp[i]还是无穷大说明这个点根本到不了直接跳过否则枚举i的所有约数更新dp[i 约数]。这里最关键的坑就是约数的范围。题目说“d不等于1且d不等于x”所以枚举的时候从2开始到根号i为止。枚举到某个约数j时它的另一侧约数是i/j这两个都有可能成为合法的跳跃步长。参考代码#include iostream #include vector #include climits using namespace std; int main() { int N, M; while (cin N M) { vectorint dp(M 1, INT_MAX); dp[N] 0; for (int i N; i M; i) { if (dp[i] INT_MAX) continue; for (int d 2; d * d i; d) { if (i % d 0) { int step1 d; int step2 i / d; if (i step1 M) { dp[i step1] min(dp[i step1], dp[i] 1); } if (step2 ! step1 i step2 M) { dp[i step2] min(dp[i step2], dp[i] 1); } } } } if (dp[M] INT_MAX) { cout -1 endl; } else { cout dp[M] endl; } } return 0; }时间复杂度是O(M乘以根号M)M范围一般不会太大完全跑得动。如果M特别大可以用预处理因数表的方法把因子枚举优化到O(M log M)但笔试场景下没必要。这块有两个特别值得说的细节。第一个是枚举约数时step2可能和step1相等比如i是4d等于2时step1和step2都是2重复更新一次不影响结果但没意义判断一下更干净。第二个是i本身可能被跳过这意味着从N出发中间有些石板是永远到不了的后续以它为跳板的所有状态都无效必须用if (dp[i] INT_MAX) continue拦住否则会拿无穷大去加一导致溢出变负数。2.2 被3整除数位和规律题意小易定义了一个数列第1项是数字1第2项是12第3项是123第4项是1234第i项就是把1到i的数字按顺序拼接起来。给定一个区间[l, r]问区间内有多少项可以被3整除。我第一次看到这题时脑子里轰的一声第r项的数字长度是O(r)级别的怎么可能真的拼出来暴力拼到一万位都会超时。所以这题肯定是找规律。判断一个大数能不能被3整除不需要真的把它算出来只需要看各个数位之和能否被3整除。第i项是“123...i”它的数位和就是1加到i也就是i乘以(i 1)除以2。于是问题变成了对于区间[l, r]有多少个i满足 i(i1)/2 mod 3 0。接下来就是纯数学。i除以3的余数只有三种情况。当i模3余1时i(i1)/2除以3的余数是1不满足当i模3余2时i1是3的倍数满足当i模3余0时i是3的倍数满足。也就是说每三个连续的数里后面两个都是满足条件的。规律非常简单。既然有了规律就能写出O(1)的计数函数。定义f(x)为1到x中满足条件的个数那么每完整的一组3个数贡献2个最后余下的1个数中如果余数是2即当前数模3等于2那么这个数也满足余数0或1都不额外贡献。所以答案是f(r) - f(l - 1)。参考代码#include iostream using namespace std; long long countThree(long long x) { if (x 0) return 0; return (x / 3) * 2 (x % 3 2 ? 1 : 0); } int main() { long long l, r; while (cin l r) { cout countThree(r) - countThree(l - 1) endl; } return 0; }这个题目的核心教训是遇到“拼接大数”“大整数运算”类的题目几乎不可能真的去构造数字。要立刻转到“能被3整除的数数位和也能被3整除”这个性质上。类似的还有“能被9整除”。还要提醒一点笔试场景里l和r的数据范围往往开得很大可能到10的18次方所以要写成long long分函数返回也用long long。很多人在这一步翻车就差一个long long导致后面AC不了。2.3 俄罗斯方块最简单的题往往最快翻车题意小易用俄罗斯方块打发时间。游戏面板有n列初始都是空的。系统会依次给出m次方块下落操作每次输入一个数字c表示一个1乘1的方块落到第c列的正上方。当某一行所有n列都有方块时这一行会消去小易获得1分。问最终获得多少分。这道题看起来像个模拟游戏其实一句代码就能解统计每一列的方块总数最终得分就是所有列方块数量的最小值。我来说说这个结论怎么来的。一次游戏过程中第c列的方块数不断增加。当某一列的高度比其他列矮时这个矮列对应的那些行就永远凑不满n个方块也就无法消除。所以能消除的行数不可能超过最矮列的高度。反过来只要所有列的高度都达到h那么前h行都能消除。因此可消除的行数就等于所有列高度的最小值。举个例子n等于3各列方块数分别是3、2、4那么能消除的行数就是2。不管这2行是哪些方块组成的只要每一列都至少有2个方块这2行就必然被填满了。参考代码#include iostream #include vector using namespace std; int main() { int n, m; cin n m; vectorint cnt(n 1, 0); for (int i 0; i m; i) { int c; cin c; cnt[c]; } int ans cnt[1]; for (int i 2; i n; i) { if (cnt[i] ans) ans cnt[i]; } cout ans endl; return 0; }为什么说最简单的题往往最快翻车因为笔试紧张的时候大家容易把简单题想复杂。有人会试图模拟每次落块后的整行消去过程维护一个二维数组还要考虑消行后上方方块下落越写越复杂最后交上去还超时。这道题给了我们一个很重要的提示碰到描述里带“游戏”“操作”的题目先想想有没有数学表达式能直接代替模拟再决定要不要真的模拟。很多题目出题人故意把包装做得复杂就是为了看你能不能一眼看穿本质。2.4 小易的字典贪心加单调栈题意给定一个由小写字母组成的字符串s和一个整数k请问从s中删除恰好k个字符后剩下的字符按原顺序组成的新字符串字典序最小是什么。删除k个字符后字典序最小这是一个非常经典的贪心模型。但它确实适合做笔试压轴题因为很多人第一次接触时不知道从哪里下手。暴力做法是枚举删掉哪些字符也就是组合数C(n, k)n稍微大一点就直接爆炸。正确解法是用单调栈维护一个字典序最小序列。核心思想是我们最终要保留n减k个字符。在从左往右扫描s的过程中如果当前字符比栈顶字符小而且我们还有剩余的删除次数就可以把栈顶字符删掉因为它放在这里会使得整个序列的字典序变大。删掉栈顶后继续与新的栈顶比较直到栈顶不大于当前字符或者删除次数用完。扫描完所有字符后如果删除次数还有剩余说明当前得到的序列已经是一个非递减序列此时从尾部多删几个字符即可。这个方法的时间复杂度是O(n)空间复杂度O(n)是笔试里最优的解法。参考代码#include iostream #include stack #include algorithm using namespace std; int main() { string s; int k; while (cin s k) { stackchar st; for (char c : s) { while (!st.empty() k 0 st.top() c) { st.pop(); k--; } st.push(c); } while (k 0 !st.empty()) { st.pop(); k--; } string res; while (!st.empty()) { res st.top(); st.pop(); } reverse(res.begin(), res.end()); cout res endl; } return 0; }这里有一个容易困惑的地方为什么最后还剩删除次数时要从尾部删因为当我们扫描完整个字符串栈里的字符序列已经满足从栈底到栈顶单调不减。此时如果必须再删除一些字符删掉任何一个中间字符后面的字符都会前移从而让字典序变大。而删掉末尾字符不会影响前面部分所以从尾部删除是最优的。我在实际写这个题的时候踩过一个坑栈里的字符弹出来以后最后组合字符串时要倒序。因为栈是先进后出栈底对应字符串的前面部分。如果忘记reverse输出的字符串就是反的提交后全是WA。这个问题非常好排查但第一次遇到时确实容易懵。2.5 瞌睡滑动窗口求最大兴趣值题意小易上课时每分钟都有一个兴趣值。他一开始在某些分钟是清醒的有些分钟是困倦的。小易只能叫醒自己一次持续k分钟。问在这k分钟内他能获得的最大兴趣值是多少。注意清醒时段的兴趣值本来就计入困倦时段只有在被叫醒的k分钟内才会计入。这一题属于典型的“固定长度滑动窗口”但包装得有点生活化考试时容易在窗口的边界处理上犯迷糊。先算一个基础值所有本来就清醒的分钟对应的兴趣值总和。这部分一定可以拿到不管叫不叫醒。然后要考虑的是在困倦的分钟里如果选择在某个时间点叫醒自己持续k分钟那么这k分钟里所有困倦分钟的兴趣值都能被加上。所以问题变成找一个长度为k的连续窗口使得窗口内所有困倦分钟的兴趣值之和最大。处理方法是把困倦分钟对应的兴趣值单独挑出来原数组中清醒分钟的位置当0处理然后求固定长度为k的最大子数组和。参考代码#include iostream #include vector using namespace std; int main() { int n, k; cin n k; vectorint a(n), sleep(n); int base 0; for (int i 0; i n; i) { cin a[i]; } for (int i 0; i n; i) { cin sleep[i]; if (sleep[i] 1) { base a[i]; } } int cur 0; for (int i 0; i k i n; i) { if (sleep[i] 0) { cur a[i]; } } int maxGain cur; for (int i k; i n; i) { if (sleep[i - k] 0) { cur - a[i - k]; } if (sleep[i] 0) { cur a[i]; } if (cur maxGain) { maxGain cur; } } cout base maxGain endl; return 0; }滑动窗口的代码本身不难难在边界。比如k可能比n大此时窗口覆盖整个数组滑动循环根本不会进入所以初始化窗口时要加if (i n)的判断。再比如计算窗口增益时只处理sleep为0的分钟如果sleep为1说明本来就清醒已经算在base里了不能重复计算。还有一个细节base和maxGain都可能比较大用int有溢出的风险建议直接全部用long long。笔试中因为整数范围栽跟头太冤了。3. 实战策略90分钟里如何把题稳稳拿到分算法题会做和能拿分之间还隔着一条鸿沟那就是考场策略。我总结了几条自己用着很顺手的经验。3.1 先写暴力再优化至少保证有分很多同学有个误区觉得笔试一定要AC所有题。其实不是。网易的笔试评分标准通常按通过用例的比例给分部分题可能给出多个测试点部分通过也能拿到不少分。所以我的建议是遇到没思路的题先写一个保证正确的暴力版本提交上去拿部分分然后再优化。千万不要因为觉得暴力太丑就不写。在时间紧张的考场上能拿到的分才是真分。3.2 注意ACM模式的输入输出陷阱网易笔试用的是ACM模式也就是自己写main函数、自己处理输入输出。常见的坑有三个。第一不确定输入有几组数据时用while (cin x)这种形式直到读不到为止。第二多组数据之间可能有空格和换行干扰cin和scanf都能自动跳过空白字符所以直接用cin读就好。第三输出的格式严格匹配尤其是要求输出“每组一行”时漏了换行会WA多了换行通常没问题。另外如果涉及浮点数输出看清楚题目要求保留几位小数输出格式一般会在题目里写清楚。网易笔试还是以整数输出为主相对友好。3.3 时间分配的实战建议我建议按这个时间比例分配审题5分钟前两题各15分钟中间一题20分钟最后一题20分钟剩下15分钟检查边界和数据类型。如果一道题卡了20分钟以上还没一点头绪先跳过去写后面的题。有时候做完后面的题回来再看会自动切换思考角度反而能发现前面的突破口。这在我身上发生过很多次。3.4 关于本地调试如果平台允许本地编译调试尽量在本地IDE里把代码跑通了再粘回去。刷题时常用的一些调试手段例如枚举小规模输入的中间结果、打印dp数组、输出栈内元素都是排查问题的好工具。但在牛客这类平台上打印调试信息会导致WA所以提交前要记得把调试输出全删了。4. 复盘与长期训练从真题看网易笔试的命题趋势4.1 网易反复在考的四个方向刷了多套网易真题以后我发现它的出题范围其实非常稳定字符串与贪心、数学规律、动态规划、滑动窗口与双指针。这套2019年的真题里五道题刚好覆盖这四类可以说很有代表性。数学规律题通常给出一个序列或者几何场景真正的解法藏在“整除性质”“奇偶性”“模运算”里。这一类题一旦找到规律代码可以短到几行但找规律的过程往往需要大量纸面推导所以考试时草稿纸一定要备够。动态规划题不会出得像竞赛那么难重点是最基础的线性DP状态定义和转移方程都比较直接。备考时应该把一维DP、二维DP、背包、最长递增子序列这些基础模型练熟。4.2 刷题规划建议如果距离笔试还有1到2个月我的建议是按模块刷不要每天只刷难题。每天固定时间做两三道常规题保持手感同时针对薄弱题型集中突破。比如这周专门刷滑动窗口下周专门刷DP。每次做完题必须复盘不能只求AC。复盘的核心是搞清楚我一开始为什么没思路是没想到这个知识点还是想到了但不会写边界把思路的断层补上下次遇到类似题型才能举一反三。4.3 常见问题速查表问题表现常见原因排查方法本地运行正常提交全WA输入输出格式不对检查是否有多余输出、格式是否匹配答案少了一部分边界条件没考虑比如i越界、k为0手推几个极端小样例运行超时算法复杂度太高看是否做了不必要的循环或重复计算数据范围大了就WA用了int溢出换long long并检查所有参与计算的变量类型输出顺序反了栈、队列的遍历顺序不清楚在本地打印中间结果确认方向这几个问题基本覆盖了我在笔试和陪练中见过的绝大多数翻车现场。其实大部分WA不是算法思路错而是细节没处理好。笔试前把这些细节检查一遍比多刷十道题更管用。4.4 如何用好这套真题这套2019年秋招真题我的建议是至少刷三遍。第一遍按真实笔试流程限时做检验自己的水平第二遍逐题深挖确保每道题都能独立推导出解法第三遍重点看错题把每道题的易错点写下来考前翻一翻。刷的过程中不要只盯着这五道题。可以把每道题延伸出来的知识点继续挖下去。比如跳石板那题可以延伸复习线性DP和枚举优化被3整除可以延伸复习模运算和数学归纳小易的字典可以延伸复习单调栈的更多应用场景。这样一来一套真题的复习效率会高很多。网易笔试的风格这些年一直延续着“经典题目加生活化包装”的路子只要基础扎实、见过足够多题型、考场心态稳拿高分并不难。这套2019年的真题确实是一份特别合适的备考起点。我自己在复盘过程中最大的体会是很多时候打败自己的不是题难而是没把简单题做对。把边界条件、数据类型、输入输出这三件事刻在脑子里就已经超过很多对手了。
返回列表